From 776083144e78014d1e3072ccfc0d0bb48230b219 Mon Sep 17 00:00:00 2001 From: cvelazquez Date: Wed, 28 Feb 2024 08:27:18 -0300 Subject: [PATCH 1/3] feat: Add Paraguayan Spanish translation files Added Paraguayan Spanish translations and updated the language dropdown to include this new locale. Additionally, configured the translation's loading within the i18n file. --- fastapi_admin/i18n.py | 1 + .../locales/es_PY/LC_MESSAGES/messages.mo | Bin 0 -> 3349 bytes .../locales/es_PY/LC_MESSAGES/messages.po | 249 ++++++++++++++++++ .../templates/components/language.html | 4 + 4 files changed, 254 insertions(+) create mode 100644 fastapi_admin/locales/es_PY/LC_MESSAGES/messages.mo create mode 100644 fastapi_admin/locales/es_PY/LC_MESSAGES/messages.po diff --git a/fastapi_admin/i18n.py b/fastapi_admin/i18n.py index f1b3451..e815fff 100644 --- a/fastapi_admin/i18n.py +++ b/fastapi_admin/i18n.py @@ -8,6 +8,7 @@ from fastapi_admin.template import templates TRANSLATIONS = { "zh_CN": Translations.load(os.path.join(BASE_DIR, "locales"), locales=["zh_CN"]), "en_US": Translations.load(os.path.join(BASE_DIR, "locales"), locales=["en_US"]), + "es_PY": Translations.load(os.path.join(BASE_DIR, "locales"), locales=["es_PY"]), "fr_FR": Translations.load(os.path.join(BASE_DIR, "locales"), locales=["fr_FR"]), "fa_IR": Translations.load(os.path.join(BASE_DIR, "locales"), locales=["fa_IR"]), } diff --git a/fastapi_admin/locales/es_PY/LC_MESSAGES/messages.mo b/fastapi_admin/locales/es_PY/LC_MESSAGES/messages.mo new file mode 100644 index 0000000000000000000000000000000000000000..a909f510ea2f35de8e0813a03c8474e79c751726 GIT binary patch literal 3349 zcmaKuUx-}Q9mkJon{;Z^YPCeI^~4a|)ZESPXsJw$WOg%&E3=zrcMTXN!`XY!%$|7f zIg@kFon!-rJ`{ovKJ>x16cUKVhtQ`~p;{~`Q>cP}K=4h3QfTv7@j($mzrTCW?A*y( zmznw8-#NeY`*+Uy-TnCXTV7!J`vtx~#rGdKku?5GZ)WUvh#B}@@Ymoj@M4gE2mTE5 zr69i!&O;u89NcjWW4D1*;6bnf-VdGue-1tm?f_o}Zw227^1I* zPRL(?cYrs;De3P9$>06pJzyRDKDY$p!%hWRfC!ysAg#9`t-A=~!+wt+n!gNE+D>JmB)u2I{8fwMo>&yJ~7a&NfGO3~O%cy`>F9>%SVMc>C8 zf)Aus-A&#VP*2!GsoXX_rNbECH|13(-G0Xl9^jiM=dsY-+f7Vb>3nCTkr@l8q|TNM zytq8|j(1J26Z~54q_FV@_wu}FtK~BsIpyW_^fX)DFz1x+!D!c-j2s&BZt!Hf#gi8) z1gX7MaIu=lv2>2dWfAsG#c<@Iux~0A>k_K$sSvBt>1GkI=z3|P;ACucLRp0;CStFQ zc5FRQpY4oAP%Co$m;1UIlQ_r2WTENNZVIr_)YtCRxqJ<}=AoGxC`K!CpvcoV>(xuWws1aLvqHO6cw-y< z*puxxUurJ4_@fi9AFH*6?&a9>2A8h0{M42ltx1uELH=mFeI!-W#`YeoEvLB^X><(D z#5K6?7os~{Kg7$&!&92?Im~DGAF3@bEVjm#nVp`g%^U4;e519^K0HznIEeG1^dXK$ zWS#VfbKi{~8S7CFxG_;n$0k8zXz)kYm9HI_T3QqeCEaZBIlAe*K0S+1t>}lA_bpnG zE&HKz-iid5w(N_-vL;%P8W=7@Z^RHKgkrIFXoxBnu^PUtc_R5%%bag6ugyQ+L|;tP z;me^{(qhO4O^G88rB$p9EpyTp7RK#!AknQ3@O~~6>7Yr)5Ee^sXrhH4&xTiuCh8OlYslP=8%CR12Mu$IxK~9*YKx zi0NtEU_~Mv8$Nel!L~??9!q_5dy8E)NMUnKvp_d ziPUUlCKXjBu5&rSjmOroCj9hZlZFhNGZyt{tyJL^i_xMVQ~b0OjGA&$uWvmHY#wiq zVr_9U*b+Ibx~Jn;L{J^9#$642n<85tUg=HqRn^N?kpb$0u!`)t&>rJrT+$m7AI<-t zx{RhPCabYj>KvkIqJ&oA)}aMTugWq`CR=f}Etl#>tl&*;6JgPaPns0}uLRKjEp8h- zo(obKk#BMlHqUr#D#L?veR(^=b+AYTceb)GEj@t9T8YB-#Od_8NYw=>RMT8C**gB$ S!Qsvw+$*zH51dTBZSrdraL3jF literal 0 HcmV?d00001 diff --git a/fastapi_admin/locales/es_PY/LC_MESSAGES/messages.po b/fastapi_admin/locales/es_PY/LC_MESSAGES/messages.po new file mode 100644 index 0000000..6e2911e --- /dev/null +++ b/fastapi_admin/locales/es_PY/LC_MESSAGES/messages.po @@ -0,0 +1,249 @@ +# Spanish (Paraguay) translations for PROJECT. +# Copyright (C) 2024 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2024-02-21 12:21-0300\n" +"PO-Revision-Date: 2024-02-21 12:22-0300\n" +"Last-Translator: FULL NAME \n" +"Language: es_PY\n" +"Language-Team: es_PY \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.14.0\n" + +#: fastapi_admin/middlewares.py:77 +#, python-format +msgid "You can only try after %(seconds)s seconds" +msgstr "Solo puedes intentarlo después de %(seconds)s segundos" + +#: fastapi_admin/middlewares.py:88 +#, python-format +msgid "Login failed %(times)s times, you can try %(rest_times)s more times" +msgstr "Fallo en el inicio de sesión %(times)s veces, puedes intentarlo %(rest_times)s veces más" + +#: fastapi_admin/resources.py:120 +msgid "create" +msgstr "Agregar nuevo" + +#: fastapi_admin/resources.py:132 +msgid "update" +msgstr "Editar" + +#: fastapi_admin/resources.py:139 +msgid "delete" +msgstr "Borrar" + +#: fastapi_admin/resources.py:149 +msgid "delete_selected" +msgstr "Eliminar seleccionados" + +#: fastapi_admin/providers/import_export.py:106 +#: fastapi_admin/templates/providers/import_export/import.html:6 +#: fastapi_admin/templates/providers/import_export/import.html:30 +msgid "Import" +msgstr "Importar" + +#: fastapi_admin/providers/import_export.py:117 +#: fastapi_admin/templates/providers/import_export/export.html:6 +#: fastapi_admin/templates/providers/import_export/export.html:23 +msgid "Export" +msgstr "Exportar" + +#: fastapi_admin/providers/import_export.py:176 +#, python-format +msgid "Success create %(count)s %(label)s" +msgstr "Éxito al crear %(count)s %(label)s" + +#: fastapi_admin/providers/login.py:136 +msgid "captcha_error" +msgstr "Error en el captcha" + +#: fastapi_admin/providers/login.py:153 +msgid "Google recaptcha verify failed" +msgstr "La verificación de Google reCAPTCHA falló" + +#: fastapi_admin/providers/login.py:160 +msgid "login_failed" +msgstr "Fallo en el inicio de sesión" + +#: fastapi_admin/providers/login.py:234 +msgid "confirm_password_different" +msgstr "Las contraseñas no coinciden" + +#: fastapi_admin/providers/login.py:269 +msgid "old_password_error" +msgstr "Error en la contraseña antigua" + +#: fastapi_admin/providers/login.py:271 +msgid "new_password_different" +msgstr "La nueva contraseña no coincide" + +#: fastapi_admin/templates/create.html:15 +#: fastapi_admin/templates/update.html:17 +msgid "save" +msgstr "Guardar" + +#: fastapi_admin/templates/create.html:17 +msgid "save_and_add_another" +msgstr "Guardar y agregar otro" + +#: fastapi_admin/templates/create.html:19 +#: fastapi_admin/templates/providers/import_export/export.html:25 +#: fastapi_admin/templates/providers/import_export/import.html:32 +#: fastapi_admin/templates/update.html:27 +msgid "return" +msgstr "Volver" + +#: fastapi_admin/templates/init.html:10 +msgid "Create first admin" +msgstr "Crear primer administrador" + +#: fastapi_admin/templates/init.html:12 +#: fastapi_admin/templates/providers/login/login.html:17 +msgid "username" +msgstr "Nombre de usuario" + +#: fastapi_admin/templates/init.html:14 +#: fastapi_admin/templates/providers/login/login.html:19 +msgid "username_placeholder" +msgstr "Ingrese el nombre de usuario" + +#: fastapi_admin/templates/init.html:18 +#: fastapi_admin/templates/providers/login/login.html:23 +msgid "password" +msgstr "Contraseña" + +#: fastapi_admin/templates/init.html:23 +#: fastapi_admin/templates/providers/login/login.html:28 +msgid "password_placeholder" +msgstr "Ingrese la contraseña" + +#: fastapi_admin/templates/init.html:31 +msgid "confirm_password" +msgstr "Confirmar contraseña" + +#: fastapi_admin/templates/init.html:36 +msgid "confirm_password_placeholder" +msgstr "Ingrese la contraseña nuevamente" + +#: fastapi_admin/templates/init.html:43 +#: fastapi_admin/templates/providers/login/password.html:32 +msgid "submit" +msgstr "Enviar" + +#: fastapi_admin/templates/list.html:13 +msgid "show" +msgstr "Mostrar" + +#: fastapi_admin/templates/list.html:24 +msgid "entries" +msgstr "entradas" + +#: fastapi_admin/templates/list.html:33 +msgid "search" +msgstr "Buscar" + +#: fastapi_admin/templates/list.html:46 +msgid "bulk_actions" +msgstr "Acciones masivas" + +#: fastapi_admin/templates/list.html:127 +msgid "actions" +msgstr "Acciones" + +#: fastapi_admin/templates/list.html:158 +#, python-format +msgid "Showing %(from)s to %(to)s of %(total)s entries" +msgstr "Mostrando %(from)s a %(to)s de %(total)s entradas" + +#: fastapi_admin/templates/list.html:169 +msgid "prev_page" +msgstr "Pág. Anterior" + +#: fastapi_admin/templates/list.html:190 +msgid "next_page" +msgstr "Pág. Siguiente" + +#: fastapi_admin/templates/update.html:24 +msgid "save_and_return" +msgstr "Guardar y volver" + +#: fastapi_admin/templates/errors/401.html:21 +#: fastapi_admin/templates/errors/403.html:21 +#: fastapi_admin/templates/errors/404.html:21 +#: fastapi_admin/templates/errors/500.html:21 +msgid "return_home" +msgstr "Volver a inicio" + +#: fastapi_admin/templates/providers/import_export/export.html:11 +#: fastapi_admin/templates/providers/import_export/import.html:18 +msgid "Format" +msgstr "Formato" + +#: fastapi_admin/templates/providers/import_export/import.html:14 +msgid "File to import" +msgstr "Archivo a importar" + +#: fastapi_admin/templates/providers/login/avatar.html:20 +#: fastapi_admin/templates/providers/login/password.html:6 +msgid "update_password" +msgstr "Actualizar contraseña" + +#: fastapi_admin/templates/providers/login/avatar.html:23 +msgid "logout" +msgstr "Cerrar sesión" + +#: fastapi_admin/templates/providers/login/login.html:37 +msgid "captcha" +msgstr "Captcha" + +#: fastapi_admin/templates/providers/login/login.html:45 +msgid "captcha_placeholder" +msgstr "Ingrese el captcha" + +#: fastapi_admin/templates/providers/login/login.html:61 +msgid "remember_me" +msgstr "Recordarme" + +#: fastapi_admin/templates/providers/login/login.html:65 +msgid "sign_in" +msgstr "Iniciar sesión" + +#: fastapi_admin/templates/providers/login/password.html:12 +msgid "old_password" +msgstr "Contraseña antigua" + +#: fastapi_admin/templates/providers/login/password.html:15 +msgid "old_password_placeholder" +msgstr "Ingrese la contraseña antigua" + +#: fastapi_admin/templates/providers/login/password.html:19 +msgid "new_password" +msgstr "Nueva contraseña" + +#: fastapi_admin/templates/providers/login/password.html:22 +msgid "new_password_placeholder" +msgstr "Ingrese la nueva contraseña" + +#: fastapi_admin/templates/providers/login/password.html:26 +msgid "re_new_password" +msgstr "Repetir nueva contraseña" + +#: fastapi_admin/templates/providers/login/password.html:29 +msgid "re_new_password_placeholder" +msgstr "Ingrese la nueva contraseña nuevamente" + +#: fastapi_admin/templates/providers/search/search.html:11 +msgid "Search in site..." +msgstr "Buscar en el sitio..." + +#: fastapi_admin/templates/providers/search/search_result.html:9 +msgid "No results found for search text" +msgstr "No se encontraron resultados para el texto de búsqueda" diff --git a/fastapi_admin/templates/components/language.html b/fastapi_admin/templates/components/language.html index 3a8c7a4..9bce6d6 100644 --- a/fastapi_admin/templates/components/language.html +++ b/fastapi_admin/templates/components/language.html @@ -30,5 +30,9 @@ href="{{ {'language':'fa_IR'}|current_page_with_params }}"> 🇮🇷Persian + + 🇵🇾Español + From fa4fa47fd5891288aaf497ac53f59368d0e92c7b Mon Sep 17 00:00:00 2001 From: cvelazquez Date: Wed, 28 Feb 2024 08:45:53 -0300 Subject: [PATCH 2/3] feat: Add optional ordering feature for resources Implemented optional ordering of resources in FastAPI admin. Code has been added to accept an 'order_by' parameter in the resource route, if present the list of resources will be sorted accordingly. Templates were updated to support this feature by adding a clickable link to each column header for sorting. --- fastapi_admin/routes/resources.py | 5 +++++ fastapi_admin/templates/list.html | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/fastapi_admin/routes/resources.py b/fastapi_admin/routes/resources.py index 42c7726..ebdda04 100644 --- a/fastapi_admin/routes/resources.py +++ b/fastapi_admin/routes/resources.py @@ -1,3 +1,5 @@ +from typing import Optional + from fastapi import APIRouter, Depends, Path from jinja2 import TemplateNotFound from starlette.requests import Request @@ -25,6 +27,7 @@ async def list_view( resource: str = Path(...), page_size: int = 10, page_num: int = 1, + order_by: Optional[str] = None, ): fields_label = model_resource.get_fields_label() fields = model_resource.get_fields() @@ -33,6 +36,8 @@ async def list_view( params, qs = await model_resource.resolve_query_params(request, dict(request.query_params), qs) filters = await model_resource.get_filters(request, params) total = await qs.count() + if order_by: + qs = qs.order_by(order_by) if page_size: qs = qs.limit(page_size) else: diff --git a/fastapi_admin/templates/list.html b/fastapi_admin/templates/list.html index a65bb07..6006b20 100644 --- a/fastapi_admin/templates/list.html +++ b/fastapi_admin/templates/list.html @@ -88,8 +88,22 @@ /> {% endif %} - {% for label in fields_label %} - {{ label }} + {% for field in fields %} + + {% set current_order = request.query_params.get('order_by', '') %} + {% set is_desc = current_order.startswith('-') %} + {% if current_order.lstrip('-') == field.name %} + {% set order_by = '-' + field.name if not is_desc else field.name %} + {% else %} + {% set order_by = field.name %} + {% endif %} + + {{ field.label }} + {% if current_order.lstrip('-') == field.name %} + {% if is_desc %}▼{% else %}▲{% endif %} + {% endif %} + + {% endfor %} {% if model_resource.actions %} From 18e2a7cf6a93db519d442e65a50f3824f0622fd8 Mon Sep 17 00:00:00 2001 From: cvelazquez Date: Thu, 7 Mar 2024 08:55:13 -0300 Subject: [PATCH 3/3] fix: Relocate navbar-toggler button in layout template The navigation bar 'navbar-toggler' button has been shifted in the layout.html file. Previously, it was located inside the 'navbar navbar-expand-md navbar-light' div, but now it resides just above the 'collapse navbar-collapse' div. This modification enhances the positioning and visibility of the button, potentially improving the user interface. --- fastapi_admin/templates/layout.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fastapi_admin/templates/layout.html b/fastapi_admin/templates/layout.html index 3ed58ed..f6c1330 100644 --- a/fastapi_admin/templates/layout.html +++ b/fastapi_admin/templates/layout.html @@ -15,6 +15,12 @@ {% endif %} +