From fb91d9ca1afdebdc780496272eefaa461350fb37 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Tue, 25 Jul 2023 12:53:12 -0400 Subject: [PATCH 01/10] Improve the language.py with changes from MADE and UpToCode, expand list of translated native-language names. See #218, #590 --- .../data/questions/al_language.yml | 0 .../AssemblyLine/data/sources/languages.yml | 141 +++++++++++++ docassemble/AssemblyLine/language.py | 191 +++++++++++++++--- docassemble/AssemblyLine/test_language.py | 50 +++++ 4 files changed, 357 insertions(+), 25 deletions(-) create mode 100644 docassemble/AssemblyLine/data/questions/al_language.yml create mode 100644 docassemble/AssemblyLine/data/sources/languages.yml create mode 100644 docassemble/AssemblyLine/test_language.py diff --git a/docassemble/AssemblyLine/data/questions/al_language.yml b/docassemble/AssemblyLine/data/questions/al_language.yml new file mode 100644 index 00000000..e69de29b diff --git a/docassemble/AssemblyLine/data/sources/languages.yml b/docassemble/AssemblyLine/data/sources/languages.yml new file mode 100644 index 00000000..50dd0b93 --- /dev/null +++ b/docassemble/AssemblyLine/data/sources/languages.yml @@ -0,0 +1,141 @@ +en: + name: English + native_name: English +es: + name: Spanish + native_name: Español +zh: + name: Chinese + native_name: 中文 +fr: + name: French + native_name: Français +de: + name: German + native_name: Deutsch +ru: + name: Russian + native_name: Русский +hi: + name: Hindi + native_name: हिन्दी +ar: + name: Arabic + native_name: العربية +ja: + name: Japanese + native_name: 日本語 +ko: + name: Korean + native_name: 한국어 +vi: + name: Vietnamese + native_name: Tiếng Việt +it: + name: Italian + native_name: Italiano +pt: + name: Portuguese + native_name: Português +tl: + name: Tagalog + native_name: Wikang Tagalog +pl: + name: Polish + native_name: Polski +nl: + name: Dutch + native_name: Nederlands +el: + name: Greek + native_name: Ελληνικά +ht: + name: Haitian Creole + native_name: Kreyòl ayisyen +zh-Hans: + name: Mandarin (Simplified) + native_name: 普通话 +zh-Hant: + name: Cantonese (Traditional) + native_name: 粵語 +kea: + name: Cape Verdean Creole + native_name: Kriolu +bn: + name: Bengali + native_name: বাংলা +so: + name: Somali + native_name: Soomaali +hy: + name: Armenian + native_name: Հայերեն +hmn: + name: Hmong + native_name: Hmoob +km: + name: Khmer + native_name: ភាសាខ្មែរ +om: + name: Oromo + native_name: Afaan Oromoo +am: + name: Amharic + native_name: አማርኛ +ne: + name: Nepali + native_name: नेपाली +lo: + name: Lao + native_name: ພາສາລາວ +my: + name: Burmese + native_name: ဗမာစာ +ps: + name: Pashto + native_name: پښتو +ku: + name: Kurdish + native_name: Kurdî +ta: + name: Tamil + native_name: தமிழ் +az: + name: Azerbaijani + native_name: Azərbaycan +yo: + name: Yoruba + native_name: Yorùbá +te: + name: Telugu + native_name: తెలుగు +uz: + name: Uzbek + native_name: Oʻzbek +pa: + name: Punjabi + native_name: ਪੰਜਾਬੀ +fa: + name: Persian (Farsi) + native_name: فارسی +sw: + name: Swahili + native_name: Kiswahili +ig: + name: Igbo + native_name: Asụsụ Igbo +ha: + name: Hausa + native_name: هَوُسَ +si: + name: Sinhalese + native_name: සිංහල +gu: + name: Gujarati + native_name: ગુજરાતી +kn: + name: Kannada + native_name: ಕನ್ನಡ +ml: + name: Malayalam + native_name: മലയാളം diff --git a/docassemble/AssemblyLine/language.py b/docassemble/AssemblyLine/language.py index fc8ed1f3..19c893ff 100644 --- a/docassemble/AssemblyLine/language.py +++ b/docassemble/AssemblyLine/language.py @@ -1,55 +1,196 @@ # coding=utf-8 -from docassemble.base.functions import url_action +from typing import List, Optional, Tuple +from docassemble.base.util import url_action, path_and_mimetype +import yaml +import pycountry __all__ = [ "get_tuples", + "get_language_list_dropdown", + "get_language_list_dropdown_item", "get_language_list", "get_language_list_item", ] -def get_tuples(lang_codes): - """Returns a list of tuples representing the language name, followed by language ISO 639-1 code. - Right now only lists languages in use by Massachusetts Defense for Eviction (MADE). +def get_tuples( + lang_codes: List[str], languages_path: Optional[str] = None +) -> List[Tuple[str, str]]: """ - long_langs = { - "en": "English", - "es": "Español", - "vi": "Tiếng Việt", - "ht": "Kreyòl", - "zh-t": "中文", - "pt": "Português", - } - tuples = [] - for lang in lang_codes: - if lang in long_langs: - tuples.append((long_langs[lang], lang)) - return tuples + Returns a list of tuples representing the language name, followed by language ISO 639-1 code. + It will use the native_name value from the languages.yml file if available, otherwise it will use the + English name from pycountry. If neither is present, it will use the language code itself. -def get_language_list(languages, current=""): - """given a list of ordered tuples with (Description, language_code), returns - an Bootstrap-formatted unordered inline list. The current language will not be a link. + Args: + lang_codes: a list of ISO 639-1 language codes (e.g. ['en', 'es']) + languages_path: the path to the languages.yml file (defaults to data/sources/languages.yml) + + Returns: + A list of tuples representing the language name, followed by language ISO 639-1 code. + + """ + if not languages_path: + languages_path = path_and_mimetype("data/sources/languages.yml")[0] + + if languages_path is not None: + with open(languages_path, "r", encoding="utf-8") as stream: + # use our hand-built dictionary matching languages to native name for languages + # if it is available + languages = yaml.safe_load(stream) + tuples = [] + for lang in lang_codes: + # English name + try: + english_name = pycountry.languages.get(alpha_2=lang).name + except: + english_name = lang + tuples.append((languages[lang].get("native_name", english_name), lang)) + return tuples + else: + # Get the english name for the language code from pycountry as a fallback + return [ + ( + pycountry.languages.get(alpha_2=lang).name + if pycountry.languages.get(alpha_2=lang) + else lang, + lang, + ) + for lang in lang_codes + ] + + +def get_language_list_dropdown( + lang_codes: List[str], + current: str = "", + languages_path: Optional[str] = None, + event_name="change_language", + icon="fa-solid fa-language", +) -> str: + """ + Get a Bootstrap 5 dropdown menu for language selection that can be added to navigation bar. + + Args: + lang_codes: a list of ISO 639-1 language codes (e.g. ['en', 'es']) + current: the current language code + languages_path: the path to the languages.yml file (defaults to data/sources/languages.yml) + event_name: the name of the event to trigger when the language is changed + icon: the name of the icon to use for the dropdown menu (defaults to fa-solid fa-language) + + Returns: + A string containing the HTML for a dropdown menu for language selection. + """ + list_start = f""" + """ + if not languages_path: + languages_path = path_and_mimetype("data/sources/languages.yml")[0] + languages = get_tuples(lang_codes, languages_path=languages_path) + + for language in languages: + if language[1] == current: + list_start += get_language_list_dropdown_item( + language, link=False, event_name=event_name + ) + else: + list_start += get_language_list_dropdown_item( + language, event_name=event_name + ) + return list_start + list_end + + +def get_language_list_dropdown_item( + language: Tuple[str, str], link: bool = True, event_name="change_language" +) -> str: + """Given an ordered tuple, returns a link to the current interview with lang=language code and the link title + given in the first part of the tuple. + + Args: + language: a tuple containing the language name and language code + link: whether to return a link or just the text + languages_path: the path to the languages.yml file (defaults to data/sources/languages.yml) + event_name: the name of the event to trigger when the language is changed + + Returns: + A string containing the HTML for a dropdown menu item for language selection. + """ + + if link: + return f"""{ language[0] }""" + else: + return f'{ language[0] }' + + +def get_language_list( + languages: Optional[List[Tuple[str, str]]] = None, + current="", + lang_codes: Optional[List[str]] = None, + languages_path: Optional[str] = None, + event_name="change_language", +) -> str: """ + Given a list of language codes, returns + a Bootstrap-formatted unordered inline list. The current language will not be a link. + + Deprecated behavior: instead of a list of language codes, you can provide list of + tuples containing the language name and language code. This is deprecated and may be removed in a future version. + + Args: + languages: a list of tuples containing the language name and language code (deprecated) + lang_codes: a list of ISO 639-1 language codes (e.g. ['en', 'es']) + current: the current language code + languages_path: the path to the languages.yml file (defaults to data/sources/languages.yml) + event_name: the name of the event to trigger when the language is changed + + + Returns: + A string containing the HTML for an unordered inline list of language selection. + """ + if not languages_path: + languages_path = path_and_mimetype("data/sources/languages.yml")[0] + if not languages: + if not lang_codes: + raise ValueError("Either languages or lang_codes must be provided") + languages = get_tuples(lang_codes, languages_path=languages_path) + list_start = '" for language in languages: if language[1] == current: - list_start += get_language_list_item(language, link=False) + list_start += get_language_list_item( + language, link=False, event_name=event_name + ) else: - list_start += get_language_list_item(language) + list_start += get_language_list_item(language, event_name=event_name) return list_start + list_end -def get_language_list_item(language, link=True): +def get_language_list_item(language, link=True, event_name="change_language"): """Given an ordered tuple, returns a link to the current interview with lang=language code and the link title - given in the first part of the tuple.""" + given in the first part of the tuple. + + Args: + language: a tuple containing the language name and language code + link: whether to return a link or just the text + languages_path: the path to the languages.yml file (defaults to data/sources/languages.yml) + event_name: the name of the event to trigger when the language is changed + + Returns: + A string containing the HTML for an unordered inline list item for language selection. + """ li_start = '
  • ' li_end = "
  • " if link: - iurl = url_action("change_language", lang=language[1]) + iurl = url_action(event_name, lang=language[1]) return ( li_start + '', result) + self.assertIn('Français', result) + + def test_get_language_list_dropdown_item(self): + language_tuple = ('English', 'en') + result = get_language_list_dropdown_item(language_tuple) + self.assertIn('English', result) + + def test_get_language_list(self): + result = get_language_list(lang_codes=self.language_codes, languages_path=self.relative_path) + self.assertIn('