ContainsProjectRole(HttpContext request, Role role, string projectId)
diff --git a/dev-requirements.in b/dev-requirements.in
index 591cf12825..b0952e6c21 100644
--- a/dev-requirements.in
+++ b/dev-requirements.in
@@ -22,6 +22,7 @@ isort
# Documentation.
mkdocs-material
mkdocs-htmlproofer-plugin
+mkdocs-static-i18n
# Templating/Docker.
Jinja2
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 562fcf336b..911bfd2104 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile dev-requirements.in
@@ -35,6 +35,8 @@ click==8.1.7
colorama==0.4.6
# via
# -r dev-requirements.in
+ # click
+ # mkdocs
# mkdocs-material
# tox
cryptography==41.0.4
@@ -69,7 +71,7 @@ flake8-eradicate==1.5.0
# via -r dev-requirements.in
ghp-import==2.1.0
# via mkdocs
-google-auth==2.23.2
+google-auth==2.23.3
# via kubernetes
humanfriendly==10.0
# via -r dev-requirements.in
@@ -105,13 +107,16 @@ mkdocs==1.5.3
# via
# mkdocs-htmlproofer-plugin
# mkdocs-material
+ # mkdocs-static-i18n
mkdocs-htmlproofer-plugin==1.0.0
# via -r dev-requirements.in
-mkdocs-material==9.4.4
+mkdocs-material==9.4.5
# via -r dev-requirements.in
mkdocs-material-extensions==1.2
# via mkdocs-material
-mypy==1.5.1
+mkdocs-static-i18n==1.1.0
+ # via -r dev-requirements.in
+mypy==1.6.0
# via -r dev-requirements.in
mypy-extensions==1.0.0
# via
@@ -166,7 +171,9 @@ pyopenssl==23.2.0
pyproject-api==1.6.1
# via tox
pyreadline3==3.4.1
- # via -r dev-requirements.in
+ # via
+ # -r dev-requirements.in
+ # humanfriendly
python-dateutil==2.8.2
# via
# ghp-import
@@ -198,12 +205,6 @@ six==1.16.0
# python-dateutil
soupsieve==2.5
# via beautifulsoup4
-tomli==2.0.1
- # via
- # black
- # mypy
- # pyproject-api
- # tox
tox==4.11.3
# via -r dev-requirements.in
types-pyopenssl==23.2.0.2
@@ -215,9 +216,7 @@ types-pyyaml==6.0.12.12
types-requests==2.31.0.8
# via -r dev-requirements.in
typing-extensions==4.8.0
- # via
- # black
- # mypy
+ # via mypy
urllib3==2.0.6
# via
# kubernetes
diff --git a/docs/user_guide/default/images/announcementBanner.png b/docs/user_guide/assets/images/announcementBanner.png
similarity index 100%
rename from docs/user_guide/default/images/announcementBanner.png
rename to docs/user_guide/assets/images/announcementBanner.png
diff --git a/docs/user_guide/default/images/characterInventoryPanel.png b/docs/user_guide/assets/images/characterInventoryPanel.png
similarity index 100%
rename from docs/user_guide/default/images/characterInventoryPanel.png
rename to docs/user_guide/assets/images/characterInventoryPanel.png
diff --git a/docs/user_guide/default/images/characterInventoryTiles.png b/docs/user_guide/assets/images/characterInventoryTiles.png
similarity index 100%
rename from docs/user_guide/default/images/characterInventoryTiles.png
rename to docs/user_guide/assets/images/characterInventoryTiles.png
diff --git a/docs/user_guide/default/images/data-entry-dup-vern-select-entry.png b/docs/user_guide/assets/images/data-entry-dup-vern-select-entry.png
similarity index 100%
rename from docs/user_guide/default/images/data-entry-dup-vern-select-entry.png
rename to docs/user_guide/assets/images/data-entry-dup-vern-select-entry.png
diff --git a/docs/user_guide/default/images/data-entry-dup-vern-select-sense.png b/docs/user_guide/assets/images/data-entry-dup-vern-select-sense.png
similarity index 100%
rename from docs/user_guide/default/images/data-entry-dup-vern-select-sense.png
rename to docs/user_guide/assets/images/data-entry-dup-vern-select-sense.png
diff --git a/docs/user_guide/default/images/data-entry-dup-vern.png b/docs/user_guide/assets/images/data-entry-dup-vern.png
similarity index 100%
rename from docs/user_guide/default/images/data-entry-dup-vern.png
rename to docs/user_guide/assets/images/data-entry-dup-vern.png
diff --git a/docs/user_guide/default/images/favicon.ico b/docs/user_guide/assets/images/favicon.ico
similarity index 100%
rename from docs/user_guide/default/images/favicon.ico
rename to docs/user_guide/assets/images/favicon.ico
diff --git a/docs/user_guide/default/images/login.png b/docs/user_guide/assets/images/login.png
similarity index 100%
rename from docs/user_guide/default/images/login.png
rename to docs/user_guide/assets/images/login.png
diff --git a/docs/user_guide/default/images/loginBanner.png b/docs/user_guide/assets/images/loginBanner.png
similarity index 100%
rename from docs/user_guide/default/images/loginBanner.png
rename to docs/user_guide/assets/images/loginBanner.png
diff --git a/docs/user_guide/default/images/mergeDefer.png b/docs/user_guide/assets/images/mergeDefer.png
similarity index 100%
rename from docs/user_guide/default/images/mergeDefer.png
rename to docs/user_guide/assets/images/mergeDefer.png
diff --git a/docs/user_guide/default/images/mergeDelete.png b/docs/user_guide/assets/images/mergeDelete.png
similarity index 100%
rename from docs/user_guide/default/images/mergeDelete.png
rename to docs/user_guide/assets/images/mergeDelete.png
diff --git a/docs/user_guide/default/images/mergeDeleted.png b/docs/user_guide/assets/images/mergeDeleted.png
similarity index 100%
rename from docs/user_guide/default/images/mergeDeleted.png
rename to docs/user_guide/assets/images/mergeDeleted.png
diff --git a/docs/user_guide/default/images/mergeEditFlag.png b/docs/user_guide/assets/images/mergeEditFlag.png
similarity index 100%
rename from docs/user_guide/default/images/mergeEditFlag.png
rename to docs/user_guide/assets/images/mergeEditFlag.png
diff --git a/docs/user_guide/default/images/mergeFlag.png b/docs/user_guide/assets/images/mergeFlag.png
similarity index 100%
rename from docs/user_guide/default/images/mergeFlag.png
rename to docs/user_guide/assets/images/mergeFlag.png
diff --git a/docs/user_guide/default/images/mergeFlagged.png b/docs/user_guide/assets/images/mergeFlagged.png
similarity index 100%
rename from docs/user_guide/default/images/mergeFlagged.png
rename to docs/user_guide/assets/images/mergeFlagged.png
diff --git a/docs/user_guide/default/images/mergeMerge.png b/docs/user_guide/assets/images/mergeMerge.png
similarity index 100%
rename from docs/user_guide/default/images/mergeMerge.png
rename to docs/user_guide/assets/images/mergeMerge.png
diff --git a/docs/user_guide/default/images/mergeMove.png b/docs/user_guide/assets/images/mergeMove.png
similarity index 100%
rename from docs/user_guide/default/images/mergeMove.png
rename to docs/user_guide/assets/images/mergeMove.png
diff --git a/docs/user_guide/default/images/mergeSaveAndContinue.png b/docs/user_guide/assets/images/mergeSaveAndContinue.png
similarity index 100%
rename from docs/user_guide/default/images/mergeSaveAndContinue.png
rename to docs/user_guide/assets/images/mergeSaveAndContinue.png
diff --git a/docs/user_guide/default/images/mergeSenseDefinitionsPartOfSpeech.png b/docs/user_guide/assets/images/mergeSenseDefinitionsPartOfSpeech.png
similarity index 100%
rename from docs/user_guide/default/images/mergeSenseDefinitionsPartOfSpeech.png
rename to docs/user_guide/assets/images/mergeSenseDefinitionsPartOfSpeech.png
diff --git a/docs/user_guide/default/images/mergeSidebar.png b/docs/user_guide/assets/images/mergeSidebar.png
similarity index 100%
rename from docs/user_guide/default/images/mergeSidebar.png
rename to docs/user_guide/assets/images/mergeSidebar.png
diff --git a/docs/user_guide/default/images/mergeSidebarMove.png b/docs/user_guide/assets/images/mergeSidebarMove.png
similarity index 100%
rename from docs/user_guide/default/images/mergeSidebarMove.png
rename to docs/user_guide/assets/images/mergeSidebarMove.png
diff --git a/docs/user_guide/default/images/mergeTwo.png b/docs/user_guide/assets/images/mergeTwo.png
similarity index 100%
rename from docs/user_guide/default/images/mergeTwo.png
rename to docs/user_guide/assets/images/mergeTwo.png
diff --git a/docs/user_guide/default/images/projectCreateTzotzil.png b/docs/user_guide/assets/images/projectCreateTzotzil.png
similarity index 100%
rename from docs/user_guide/default/images/projectCreateTzotzil.png
rename to docs/user_guide/assets/images/projectCreateTzotzil.png
diff --git a/docs/user_guide/default/images/projectLanguagesTzotzil.png b/docs/user_guide/assets/images/projectLanguagesTzotzil.png
similarity index 100%
rename from docs/user_guide/default/images/projectLanguagesTzotzil.png
rename to docs/user_guide/assets/images/projectLanguagesTzotzil.png
diff --git a/docs/user_guide/default/images/projectSchedule.png b/docs/user_guide/assets/images/projectSchedule.png
similarity index 100%
rename from docs/user_guide/default/images/projectSchedule.png
rename to docs/user_guide/assets/images/projectSchedule.png
diff --git a/docs/user_guide/default/images/projectSettings1Basic.png b/docs/user_guide/assets/images/projectSettings1Basic.png
similarity index 100%
rename from docs/user_guide/default/images/projectSettings1Basic.png
rename to docs/user_guide/assets/images/projectSettings1Basic.png
diff --git a/docs/user_guide/default/images/projectSettings2Langs.png b/docs/user_guide/assets/images/projectSettings2Langs.png
similarity index 100%
rename from docs/user_guide/default/images/projectSettings2Langs.png
rename to docs/user_guide/assets/images/projectSettings2Langs.png
diff --git a/docs/user_guide/default/images/projectSettings3Users.png b/docs/user_guide/assets/images/projectSettings3Users.png
similarity index 100%
rename from docs/user_guide/default/images/projectSettings3Users.png
rename to docs/user_guide/assets/images/projectSettings3Users.png
diff --git a/docs/user_guide/default/images/projectSettings4Port.png b/docs/user_guide/assets/images/projectSettings4Port.png
similarity index 100%
rename from docs/user_guide/default/images/projectSettings4Port.png
rename to docs/user_guide/assets/images/projectSettings4Port.png
diff --git a/docs/user_guide/default/images/projectSettings5Sched.png b/docs/user_guide/assets/images/projectSettings5Sched.png
similarity index 100%
rename from docs/user_guide/default/images/projectSettings5Sched.png
rename to docs/user_guide/assets/images/projectSettings5Sched.png
diff --git a/docs/user_guide/default/images/projectStatsButton.png b/docs/user_guide/assets/images/projectStatsButton.png
similarity index 100%
rename from docs/user_guide/default/images/projectStatsButton.png
rename to docs/user_guide/assets/images/projectStatsButton.png
diff --git a/docs/user_guide/default/images/reviewEntriesColumns.png b/docs/user_guide/assets/images/reviewEntriesColumns.png
similarity index 100%
rename from docs/user_guide/default/images/reviewEntriesColumns.png
rename to docs/user_guide/assets/images/reviewEntriesColumns.png
diff --git a/docs/user_guide/default/images/userMenu.png b/docs/user_guide/assets/images/userMenu.png
similarity index 100%
rename from docs/user_guide/default/images/userMenu.png
rename to docs/user_guide/assets/images/userMenu.png
diff --git a/docs/user_guide/default/images/userMenuAdmin.png b/docs/user_guide/assets/images/userMenuAdmin.png
similarity index 100%
rename from docs/user_guide/default/images/userMenuAdmin.png
rename to docs/user_guide/assets/images/userMenuAdmin.png
diff --git a/docs/user_guide/default/licenses/OFL.txt b/docs/user_guide/assets/licenses/OFL.txt
similarity index 100%
rename from docs/user_guide/default/licenses/OFL.txt
rename to docs/user_guide/assets/licenses/OFL.txt
diff --git a/docs/user_guide/default/licenses/backend_licenses.txt b/docs/user_guide/assets/licenses/backend_licenses.txt
similarity index 100%
rename from docs/user_guide/default/licenses/backend_licenses.txt
rename to docs/user_guide/assets/licenses/backend_licenses.txt
diff --git a/docs/user_guide/default/licenses/frontend_licenses.txt b/docs/user_guide/assets/licenses/frontend_licenses.txt
similarity index 100%
rename from docs/user_guide/default/licenses/frontend_licenses.txt
rename to docs/user_guide/assets/licenses/frontend_licenses.txt
diff --git a/docs/user_guide/default/static/stylesheets/extra.css b/docs/user_guide/assets/static/stylesheets/extra.css
similarity index 100%
rename from docs/user_guide/default/static/stylesheets/extra.css
rename to docs/user_guide/assets/static/stylesheets/extra.css
diff --git a/docs/user_guide/docs/account.es.md b/docs/user_guide/docs/account.es.md
new file mode 100644
index 0000000000..ac55f067ef
--- /dev/null
+++ b/docs/user_guide/docs/account.es.md
@@ -0,0 +1,45 @@
+# Cuenta
+
+Cómo registrarse, iniciar sesión y editar su cuenta.
+
+![Inicio de sesión](../images/login.png){.center}
+
+## Registrarse
+
+Para crear una nueva cuenta, pulse el botón [REGISTRARSE](../../sign-up) en la página de inicio de sesión.
+
+!!! note "Nota"
+
+ La dirección de correo electrónico se utiliza para restablecer su contraseña, por lo que sólo se permite una cuenta por dirección de correo electrónico.
+
+## Iniciar Sesión
+
+[Iniciar Sesión](../../login) a The Combine con el nombre de usuario y la contraseña facilitados en el momento de la
+inscripción.
+
+Si desea cambiar su contraseña, haga clic en el enlace "¿Olvidó su contraseña?". Siga las instrucciones y un
+restablecimiento de contraseña será enviada a la dirección de correo electrónico asociada a su cuenta.
+
+!!! warning "Importante"
+
+ El nombre de usuario y la dirección de correo electrónico **no** distinguen entre mayúsculas y minúsculas. La contraseña **es** sensible a mayúsculas y minúsculas.
+
+## Configuración
+
+Tras iniciar sesión, aparecerá una barra de aplicaciones azul en la parte superior de The Combine. Pulse el icono del
+avatar en el extremo derecho de la App Barra para abrir el Menú de Usuario. Seleccione "Configuración de usuario" para
+cambiar los ajustes de cuenta/perfil.
+
+![Menú de usuario](../images/userMenu.png){.center}
+
+Puede añadir o actualizar su:
+
+- imagen de avatar;
+- nombre;
+- número de teléfono;
+- dirección de correo electrónico;
+- lenguaje de interfaz de usuario.
+
+!!! note "Nota"
+
+ El nombre de usuario elegido en el momento de la inscripción es permanente y no puede cambiarse.
diff --git a/docs/user_guide/docs/en/account.md b/docs/user_guide/docs/account.md
similarity index 88%
rename from docs/user_guide/docs/en/account.md
rename to docs/user_guide/docs/account.md
index 15b96e1b5c..3799f31283 100644
--- a/docs/user_guide/docs/en/account.md
+++ b/docs/user_guide/docs/account.md
@@ -2,13 +2,13 @@
How to sign up, log in, and edit your account.
-![Login](images/login.png){ .center }
+![Login](images/login.png){.center}
## Sign Up
To create a new account, click the [SIGN UP](../sign-up) button on the Login page.
-!!! note
+!!! note "Note"
The email address is used to reset your password, so there is only one account allowed per email address.
@@ -19,7 +19,7 @@ To create a new account, click the [SIGN UP](../sign-up) button on the Login pag
If you want to change your password, click the "Forgot password?" link. Follow the instructions and a password-reset
will be sent to the email address associated with your account.
-!!! important
+!!! warning "Important"
The username and email address are **not** case-sensitive. The password **is** case-sensitive.
@@ -28,7 +28,7 @@ will be sent to the email address associated with your account.
After login, there is a blue App Bar along the top of The Combine. Click on the avatar icon at the right end of the App
Bar to open the User Menu. Select "User Settings" to change account/profile settings.
-![User Menu](images/userMenu.png){ .center }
+![User Menu](images/userMenu.png){.center}
You can add or update your:
@@ -38,6 +38,6 @@ You can add or update your:
- email address;
- user-interface language.
-!!! note
+!!! note "Note"
The username chosen at registration is permanent and cannot be changed.
diff --git a/docs/user_guide/docs/admin.es.md b/docs/user_guide/docs/admin.es.md
new file mode 100644
index 0000000000..f1e2e03968
--- /dev/null
+++ b/docs/user_guide/docs/admin.es.md
@@ -0,0 +1,38 @@
+# Administración / Configuración del sitio
+
+Los administradores del sitio disponen de una opción más en el menú de usuario: "Configuración del sitio".
+
+![Menú de usuario - Administrador](../images/userMenuAdmin.png){.center}
+
+## Administración de proyectos
+
+Los administradores pueden exportar o archivar/restaurar cualquier proyecto. Archivar un proyecto lo hace invisible e
+inaccesible para todos los usuarios, incluso el creador del proyecto, pero cualquier administrador puede restaurar el
+proyecto. Actualmente no hay forma de eliminar permanentemente un proyecto.
+
+## Administración de usuarios
+
+Los administradores pueden eliminar cualquier cuenta de usuario que no sea de administrador. Para añadir o eliminar
+usuarios administradores, póngase en contacto con el propietario del sitio.
+
+## Banners
+
+Los administradores pueden personalizar su instancia de The Combine mediante banners de configuración.
+
+### Banner de anuncios
+
+El banner de anuncio coloca un banner brillante en la parte superior de la página cuando los usuarios visitan The
+Combine. Estos banners están destinados a mostrar a los usuarios mensajes importantes y de corta duración relacionados
+con la instancia de The Combine. Los usos podrían incluir actualizaciones programadas, tiempos de inactividad
+planificados o próximos cambios en el servidor.
+
+![Banner de anuncios](../images/announcementBanner.png){.center}
+
+### Banner de inicio de sesión
+
+El banner de inicio de sesión coloca un mensaje en la parte inferior de la página de inicio de sesión. Este banner está
+pensado para incluir información sobre una instancia de The Combine que los usuarios puedan necesitar conocer, como con
+quién ponerse en contacto en relación con el soporte, actualizaciones de versión o políticas de copia de seguridad de la
+base de datos.
+
+![Banner de inicio de sesión](../images/loginBanner.png){width=400 .center}
diff --git a/docs/user_guide/docs/en/admin.md b/docs/user_guide/docs/admin.md
similarity index 87%
rename from docs/user_guide/docs/en/admin.md
rename to docs/user_guide/docs/admin.md
index 862d036d4b..202c2bcd10 100644
--- a/docs/user_guide/docs/en/admin.md
+++ b/docs/user_guide/docs/admin.md
@@ -2,7 +2,7 @@
Site administrators have one more option in the User Menu: "Site Settings".
-![User Menu - Admin](images/userMenuAdmin.png){ .center }
+![User Menu - Admin](images/userMenuAdmin.png){.center}
## Project Management
@@ -24,7 +24,7 @@ The announcement banner places a bright banner across the top of the page when u
are intended for displaying important, short-term messages to users related to the instance of The Combine. Uses could
include scheduled upgrades, planned down time, or upcoming server changes.
-![Announcement Banner](images/announcementBanner.png){ .center }
+![Announcement Banner](images/announcementBanner.png){.center}
### Login Banner
@@ -32,4 +32,4 @@ The login banner places a message at the bottom of the login page. This banner i
information about an instance of The Combine that users may need to know, such as who to contact regarding support,
version upgrades, or database backup policies.
-![Login Banner](images/loginBanner.png){ width=400 .center }
+![Login Banner](images/loginBanner.png){width=400 .center}
diff --git a/docs/user_guide/docs/dataEntry.es.md b/docs/user_guide/docs/dataEntry.es.md
new file mode 100644
index 0000000000..24afc3943e
--- /dev/null
+++ b/docs/user_guide/docs/dataEntry.es.md
@@ -0,0 +1,77 @@
+# Entrada de datos
+
+## Árbol de dominios semánticos
+
+Navegue o busque el dominio de su interés.
+
+!!! tip "Consejo"
+
+ Para agilizar la búsqueda de un dominio, The Combine insertará automáticamente `.` entre los dígitos consecutivos a medida que usted
+ escriba. Por ejemplo, `1234` se convertirá automáticamente en `1.2.3.4`. Este comportamiento no se produce si se introduce algún carácter no numérico
+ se introducen caracteres no numéricos.
+
+## Nueva entrada
+
+### Vernáculo
+
+Palabra tal y como se encuentra en la lengua vernácula, normalmente deletreada fonéticamente o con la ortografía local.
+
+### Glosa
+
+Aunque una entrada puede tener varios acepciones/brillos, sólo se puede añadir uno cuando se crea la entrada por primera
+vez.
+
+### Nota
+
+Puede tener una nota en cada entrada. Se puede añadir cualquier anotación para los acepciones de una entrada, glosas,
+dominios semánticos, etc a la nota de la entrada.
+
+### Grabación
+
+Puede añadir varias grabaciones a una entrada (por ejemplo, una voz masculina y otra femenina). Como en el caso de la
+nota, el audio se asocian a la entrada y no a acepciones individuales.
+
+Para grabar audio, hay un botón circular rojo. Para cada audio grabado, hay un botón triangular verde.
+
+**Con el ratón:** Pulse y mantenga pulsado el círculo rojo para grabar. Haga clic en un triángulo verde para reproducir
+su audio, o pulse Mayúsculas y haga clic para borrar su grabación.
+
+**En una pantalla táctil:** Mantenga pulsado el círculo rojo para grabar. Pulse un triángulo verde para reproducir su
+audio, o manténgalo pulsado para abrir un menú (con opciones para reproducir o eliminar).
+
+## Nueva entrada con forma vernácula duplicada {#new-entry-with-duplicate-vernacular-form}
+
+Si presenta una nueva entrada con idéntica forma vernácula y glosa a una entrada existente, se actualizará dicha entrada
+en lugar de crearse una nueva entrada. Por ejemplo, si presenta [Vernáculo: finger; Glosa: dedo] en el dominio 2.1.3.1
+(Brazo) y de nuevo en el dominio 2.1.3.3 (Dedo, dedo del pie), el resultado será una única entrada para "finger" con un
+única acepción que tiene glosa "dedo" y dos dominios.
+
+The Combine dispone de una función opcional de facilitar la introducción de palabras que ya existen en el proyecto pero
+que se recogen de nuevo en nuevo dominio semántico. Esta función puede activarse o desactivarse en
+[Configuración del proyecto > Autocompletar](project.md#autocomplete). Cuando el ajuste está activado, al escribir la
+forma vernácula en Entrada de datos, aparece un menú desplegable con formas vernáculas idénticas/similares que ya
+existen como entradas en el proyecto. Si ve que la palabra que escribe ya está en el proyecto, puede pulsar en la
+palabra en la lista de sugerencias, en lugar de tener que escribir el resto de la palabra. Cuando la configuración está
+desactivada, la palabra vernácula debe escribirse en su totalidad; no se sugerirá ninguna coincidencia potencial
+existente.
+
+![Entrada de datos duplicados de formularios vernáculos](../images/data-entry-dup-vern.png){.center}
+
+Ya sea que escriba una forma que coincide con una entrada existente en el proyecto o seleccione una de las sugerencias
+que ofrece The Combine, aparecerá un cuadro con opciones. (Este cuadro no aparecerá si la configuración de Autocompletar
+está desactivada o si escribe una forma vernáculo que aún no existe en el proyecto.) En el cuadro emergente, se le
+mostrarán todas las entradas existentes con esa forma vernácula y puede elegir si actualizar una de esas entradas o
+crear una nueva entrada.
+
+![Entrada de datos entradas vernáculas duplicadas](../images/data-entry-dup-vern-select-entry.png){.center}
+
+Si elige crear una nueva entrada, se cerrará el cuadro emergente y entonces podrá escribir la glosa de su nueva entrada.
+
+!!! note "Nota"
+
+ Aunque haya seleccionado crear una nueva entrada, si la glosa que escribe es idéntica a una glosa de otra entrada con la misma forma vernácula, no se creará una nueva entrada, sino que se actualizará esa entrada.
+
+Si por el contrario elige actualizar una de las entradas existentes, aparecerá un segundo cuadro. Aquí puede elegir
+actualizar una acepción existente en la entrada seleccionada o añadir una nueva acepción a esa entrada.
+
+![Entrada de datos duplicadas acepciones vernáculas](../images/data-entry-dup-vern-select-sense.png){.center}
diff --git a/docs/user_guide/docs/en/dataEntry.md b/docs/user_guide/docs/dataEntry.md
similarity index 91%
rename from docs/user_guide/docs/en/dataEntry.md
rename to docs/user_guide/docs/dataEntry.md
index be74bd35a8..78744dbd08 100644
--- a/docs/user_guide/docs/en/dataEntry.md
+++ b/docs/user_guide/docs/dataEntry.md
@@ -4,7 +4,7 @@
Browse or search for the domain of interest.
-!!! tip
+!!! tip "Tip"
To make searching for a domain quicker, The Combine will automatically insert `.` between consecutive digits as you
type. For example, `1234` will automatically become `1.2.3.4`. This behavior does not happen if any non-numeric
@@ -27,8 +27,8 @@ to the entry's note.
### Recording
-You can add multiple recordings to an entry (e.g., a male voice and a female voice). As with the [Note](#note), audio
-recordings are associated with the entry and not individual senses.
+You can add multiple recordings to an entry (e.g., a male voice and a female voice). As with the note, audio recordings
+are associated with the entry and not individual senses.
To record audio, there is a red circle button. For each recorded audio, there is a green triangle button.
@@ -38,7 +38,7 @@ delete its recording.
**On a touch screen:** Press-and-hold the red circle to record. Tap a green triangle to play its audio, or
press-and-hold to bring up a menu (with options to play or delete).
-## New Entry with Duplicate Vernacular Form
+## New Entry with Duplicate Vernacular Form {#new-entry-with-duplicate-vernacular-form}
If you submit a new entry with identical vernacular form and gloss to an existing entry, that entry will be updated
instead of a new entry created. For example, if you submit [Vernacular: dedo; Gloss: finger] in domain 2.1.3.1 (Arm) and
@@ -53,22 +53,22 @@ project. If you see that the word you are typing is already in the project, you
suggestions, instead of having to typing the remainder of the word. When the setting is off, the vernacular word must be
typed in its entirety; no existing potential matches will be suggested.
-![Data Entry duplicate vernacular forms](images/data-entry-dup-vern.png){ .center }
+![Data Entry duplicate vernacular forms](images/data-entry-dup-vern.png){.center}
Whether you type a form that matches an existing entry in the project or select one of the suggestions offered by The
Combine, a box will pop up with options. (This box will not pop up if the Autocomplete setting is off or if you type a
vernacular form that does not yet exist in the project.) In the pop-up box, you will be shown all existing entries with
that vernacular form and can to choose whether to update one of those entries or create a new entry.
-![Data Entry duplicate vernacular entries](images/data-entry-dup-vern-select-entry.png){ .center }
+![Data Entry duplicate vernacular entries](images/data-entry-dup-vern-select-entry.png){.center}
If you choose to create a new entry, the pop-up box will close, and you can then type the gloss for your new entry.
-!!! note
+!!! note "Note"
Even if you selected to create a new entry, if the gloss you type is identical to a gloss of another entry with the same vernacular form, a new entry will not be created, but rather that entry will be updated.
If you instead choose to update one of the existing entries, a second box will appear. Here you can choose to update an
existing sense on the selected entry or to add a new sense to that entry.
-![Data Entry duplicate vernacular senses](images/data-entry-dup-vern-select-sense.png){ .center }
+![Data Entry duplicate vernacular senses](images/data-entry-dup-vern-select-sense.png){.center}
diff --git a/docs/user_guide/docs/goals.es.md b/docs/user_guide/docs/goals.es.md
new file mode 100644
index 0000000000..8861e3cbeb
--- /dev/null
+++ b/docs/user_guide/docs/goals.es.md
@@ -0,0 +1,204 @@
+# Depuración de datos / Objetivos
+
+## Revisar entradas {#review-entries}
+
+La tabla Revisar entradas muestra todas las entradas del proyecto seleccionado.
+
+### Ordenar y filtrar columnas
+
+Las columnas son: Edición, Vernáculo, Acepciones, Glosas, Dominios, Pronunciaciones, Nota, Bandera y Borrar.
+
+![Revisar los encabezados de columna de las entradas](../images/reviewEntriesColumns.png)
+
+En la parte superior de una columna con contenido predominantemente textual (Vernáculo, Glosas, Dominios, Nota o
+Bandera), puede ordenar alfabéticamente o filtrar con una búsqueda de texto.
+
+En la parte superior de la columna Acepciones o la columna Pronunciaciones, puede ordenar o filtrar por el número de
+acepciones o pronunciaciones que tienen las entradas.
+
+Debido a la naturaleza de la recopilación rápida de palabras, la [entrada de datos](dataEntry.md) en The Combine no
+permite añadir definiciones o partes de la oración. Sin embargo, si el proyecto tiene datos importados en los que hay
+definiciones o partes de discurso, se añadirán automáticamente columnas adicionales a la tabla Entradas de revisión.
+
+### Edición de filas de entrada
+
+Puede grabar, reproducir o borrar las grabaciones de audio de una entrada con los iconos de la columna Pronunciaciones.
+Puede eliminar una entrada completa con el icono de la columna Suprimir.
+
+Para editar la forma vernácula, los acepciones (incluidas las glosas y los dominios), la nota o la bandera de una
+entrada, haga clic en el icono de la columna Editar.
+
+## Combinar duplicados {#merge-duplicates}
+
+Esta herramienta encuentra automáticamente conjuntos de entradas potencialmente duplicadas (hasta 5 entrada en cada
+conjunto, y hasta 12 conjuntos en cada pasa). Primero presenta conjuntos de palabras con idénticas formas vernáculas. A
+continuación, presenta conjuntos con formas vernáculas similares o glosas (o definiciones) idénticas.
+
+![Fusionar duplica dos entradas](../images/mergeTwo.png)
+
+Cada entrada se muestra en una columna, y cada acepción de esa entrada se muestra como una tarjeta que puede pulsar y
+arrastrar. Hay tres cosas básicas que puede hacer con una acepción: moverlo, combinarlo con otra acepción o eliminarlo.
+
+### Mover una acepción
+
+Cuando pulsa y mantiene pulsada una tarjeta de acepción, ésta se vuelve verde. Puede arrastrar y soltar la tarjeta de
+acepción a un lugar diferente de la misma columna para reordenar las acepciones de esa entrada. O puede arrastrar y
+soltar la tarjeta de acepción a una columna diferente para mover la acepción a esa otra entrada.
+
+![Fusionar duplicados moviendo una acepción](../images/mergeMove.png)
+
+Si desea dividir una entrada con varias acepciones en varias entradas, puede arrastrar una de las tarjetas de acepción a
+la columna extra vacía de la derecha.
+
+### Fusionar una acepción
+
+Si arrastra una tarjeta de acepción sobre otra tarjeta de acepción, la otra tarjeta de acepción también se vuelve verde.
+
+![Fusionar duplicados fusionar una acepción](../images/mergeMerge.png)
+
+Soltar una carta de acepción sobre otra carta de acepción (cuando ambas están verdes) fusiona las acepciones. Esto hace
+que aparezca una barra lateral azul aparezca a la derecha, mostrando cuales acepciones se están fusionando.
+
+![Fusionar duplicados acepciones fusionadas](../images/mergeSidebar.png)
+
+!!! warning "Importante"
+
+ Cuando se fusionan varias acepciones, se conservan todos los dominios semánticos, pero **sólo la acepción superior de la barra lateral** conserva sus glosas (y definiciones).
+
+Puede arrastrar y soltar tarjetas de acepción hacia o desde la barra lateral para cambiar las acepciones que se están
+fusionando. O dentro de la barra lateral, puede desplazar una acepción diferente a la parte superior (para conservar sus
+glosas).
+
+![Fusionar duplicados moviendo una acepción de la barra lateral](../images/mergeSidebarMove.png)
+
+Pulse el corchete angular derecho (>) para cerrar o abrir la barra lateral azul.
+
+### Borrar una acepción
+
+Para eliminar una acepción por completo, arrastre su tarjeta hasta el icono del cubo de basura situado en la esquina
+inferior izquierda. Cuando la tarjeta de acepción se ponga roja, suéltela.
+
+![Fusionar duplicados eliminar una acepción](../images/mergeDelete.png)
+
+Si borra la única acepción que queda de una columna, toda la columna desaparecerá, y esa entrada entera se borrada
+cuando pulse Guardar y Continuar.
+
+![Fusionar duplicados acepción borrada](../images/mergeDeleted.png)
+
+### Marcar una entrada
+
+Hay un icono de bandera en la esquina superior derecha de cada columna (a la derecha del formulario vernáculo).
+
+![Fusionar duplicados marcar una entrada](../images/mergeFlag.png){.center}
+
+Puede hacer clic en el icono de la bandera para marcar la entrada para una futura inspección o edición. (Puede
+clasificar las entradas marcadas en [Revisar entradas](#review-entries)) Cuando marque una entrada, se le dará la opción
+de añadir texto a la bandera.
+
+![Fusionar duplicados añadiendo o editando una bandera](../images/mergeEditFlag.png){.center}
+
+Tanto si se escribe texto como si no, sabrá que la entrada está marcada porque el icono de la bandera aparecerá en rojo
+sólido. Si ha añadido texto, puede pasar el cursor por encima de la bandera para ver el texto.
+
+![Fusionar Duplica una entrada marcada](../images/mergeFlagged.png){.center}
+
+Pulse sobre el icono de la bandera roja para editar el texto o eliminar la bandera.
+
+### Terminar un conjunto
+
+Hay dos botones en la parte inferior para terminar el trabajo en el conjunto actual de duplicados potenciales y pasar al
+siguiente conjunto: "Guardar y Continuar" y "Aplazar".
+
+#### Guardar y Continuar
+
+![Fusionar duplicados botón Guardar y Continuar](../images/mergeSaveAndContinue.png)
+
+El botón azul "Guardar y continuar" hace dos cosas. En primer lugar, guarda todos los cambios realizados (es decir,
+todas las acepciones movidas, fusionadas o eliminadas), actualizando las palabras en la base de datos. En segundo lugar,
+guarda las palabras no falsificadas como no duplicadas.
+
+!!! tip "Consejo"
+
+ ¿Los duplicados potenciales no son duplicados? Sólo tiene que pulsar Guardar y Continuar para decirle a The Combine que no le vuelva a mostrar ese conjunto.
+
+!!! note "Nota"
+
+ Si una de las palabras de un conjunto no fusionado intencionadamente se edita (por ejemplo, en las entradas de revisión), el conjunto puede volver a aparecer como duplicados potenciales.
+
+#### Aplazar
+
+![Fusionar duplicados botón Aplazar](../images/mergeDefer.png)
+
+El botón gris "Aplazar" restablece cualquier cambio realizado en el conjunto de duplicados potenciales. El conjunto
+aplazado se puede revisar por Revisar duplicados aplazados.
+
+### Fusión con datos importados
+
+#### Definiciones y parte de la oración
+
+Aunque las definiciones y las partes de la oración no pueden añadirse durante la introducción de datos, sí pueden estar
+presentes en las entradas importadas. Esta información aparecerá en las tarjetas de acepción de Fusionar duplicados de
+la siguiente manera:
+
+- Cualquier definición en una lengua de análisis se muestra debajo de la glosa en esa lengua.
+- Cualquier parte de la oración se indica mediante un hexágono de color en la esquina superior izquierda. El color
+ corresponde a su categoría (por ejemplo, sustantivo o verbo). Pase el cursor por encima del hexágono para ver la
+ categoría gramatical específica (p. ej., nombre propio o verbo transitivo).
+
+![Fusionar duplicados acepción con definiciones y parte de la oración](../images/mergeSenseDefinitionsPartOfSpeech.png){.center}
+
+#### Entradas y acepciones protegidas
+
+Si una entrada o acepción importada contiene datos no admitidos en The Combine (por ejemplo, etimologías o inversiones
+de sentido), se protege para evitar su eliminación. Si una acepción está protegida, su tarjeta tendrá un fondo
+amarillo—no se puede borrar ni colocar en (es decir, se combina en) otra tarjeta de acepción. Si una entrada completa
+está protegida, su columna tendrá una cabecera amarilla (donde se encuentran la lengua vernácula y la bandera). Cuando
+una entrada protegida sólo tiene una acepción, esa tarjeta de acepción no se puede mover.
+
+## Crear inventario de caracteres
+
+Las herramientas de inventario de personajes sólo están disponibles para los administradores de proyectos.
+
+_Crear un inventario de caracteres_ proporciona una visión general de cada carácter unicode que aparece en las formas
+vernáculas del entradas del proyecto. Esto le permite identificar qué caracteres se utilizan habitualmente en la lengua
+y "aceptarlos" como parte del inventario de caracteres de la lengua. El inventario de caracteres forma parte del archivo
+LDML para la lengua vernácula de un proyecto lengua vernácula que se incluye cuando se [exporta](project.md#export) el
+proyecto. La aceptación de los caracteres conducirá a una representación precisa de la lengua en Unicode, el Ethnologue
+y otros estándares y recursos lingüísticos.
+
+Otro uso de _Crear inventario de caracteres_ es identificar y sustituir caracteres que se han utilizado incorrectamente
+al escribir formas vernáculas de palabras.
+
+Hay una ficha para cada carácter unicode que aparece en la forma vernácula de cualquier entrada. Cada ficha muestra el
+carácter, su valor Unicode "U+", el número de veces que aparece en las formas vernáculas de entrada y su designación
+(por defecto: Indeciso).
+
+![Fichas de personaje del inventario](../images/characterInventoryTiles.png)
+
+### Gestionar un solo carácter
+
+Haga clic en una ficha de personaje para abrir un panel para ese personaje.
+
+!!! tip "Consejo"
+
+ Puede que tenga que desplazarse para ver el panel. Si su ventana es lo suficientemente ancha, habrá un margen en blanco a la
+ derecha; el panel estará en la parte superior de éste. Si su ventana es estrecha, los azulejos llenarán todo el lado derecho de la
+ ventana; el panel estará en la parte inferior, debajo de todos los azulejos.
+
+![Inventario de personajes panel de personajes](../images/characterInventoryPanel.png){.center}
+
+El centro del panel muestra hasta 5 ejemplos de formas vernáculas en las que aparece el carácter, resaltando el carácter
+en cada ocurrencia.
+
+En la parte superior del panel hay tres botones para designar si el carácter debe incluirse en el inventario de
+caracteres de la lengua vernácula: "ACEPTAR", "NO DECIDIDO" y "RECHAZAR". Al pulsar cualquiera de estos botones se
+actualizará la designación en la parte inferior de la ficha de personaje. (Estas actualizaciones del inventario del
+personaje no se guardan en el proyecto hasta que pulse el botón GUARDAR en la parte inferior de la página)
+
+En la parte inferior del panel se encuentra la herramienta Buscar y Reemplazar. Si _cada_ aparición del carácter debe
+sustituirse por otra cosa, escriba el carácter o cadena de sustitución en la casilla "Sustituir por" y pulse el botón
+APLICAR.
+
+!!! warning "Importante"
+
+ La operación de buscar y reemplazar realiza cambios en las entradas, no en el inventario de personajes. No **se puede deshacer**
diff --git a/docs/user_guide/docs/en/goals.md b/docs/user_guide/docs/goals.md
similarity index 95%
rename from docs/user_guide/docs/en/goals.md
rename to docs/user_guide/docs/goals.md
index 99cc10cfc1..0e0f83cdaf 100644
--- a/docs/user_guide/docs/en/goals.md
+++ b/docs/user_guide/docs/goals.md
@@ -1,6 +1,6 @@
# Data Cleanup / Goals
-## Review Entries
+## Review Entries {#review-entries}
The Review Entries table shows all of the entries in the selected project.
@@ -28,7 +28,7 @@ delete an entire entry by using the icon in the Delete column.
To edit an entry's vernacular form, senses (including glosses and domains), note, or flag, click the icon in the Edit
column.
-## Merge Duplicates
+## Merge Duplicates {#merge-duplicates}
This tool automatically finds sets of potential duplicate entries (up to 5 entries in each set, and up to 12 sets in
each pass). First it presents sets of words with identical vernacular forms. Then it presents sets with similar
@@ -61,7 +61,7 @@ to appear on the right, showing which senses are being merged.
![Merge Duplicates senses merged](images/mergeSidebar.png)
-!!! important
+!!! warning "Important"
When multiple senses are merged, all semantic domains are preserved, but **only the top sense in the sidebar** has its glosses (and definitions) preserved.
@@ -88,17 +88,17 @@ deleted when you Save & Continue.
There is a flag icon at the top-right corner of every column (to the right of the vernacular form).
-![Merge Duplicates flagging an entry](images/mergeFlag.png){ .center }
+![Merge Duplicates flagging an entry](images/mergeFlag.png){.center}
You can click on the flag icon to flag the entry for future inspection or editing. (You can sort flagged entries in
[Review Entries](#review-entries).) When you flag an entry, you are given the option to add text to the flag.
-![Merge Duplicates adding or editing a flag](images/mergeEditFlag.png){ .center }
+![Merge Duplicates adding or editing a flag](images/mergeEditFlag.png){.center}
Whether or not any text is typed, you will know that the entry is flagged because the flag icon will be solid red. If
you added text, you can hover your cursor over the flag to see the text.
-![Merge Duplicates a flagged entry](images/mergeFlagged.png){ .center }
+![Merge Duplicates a flagged entry](images/mergeFlagged.png){.center}
Click on the red flag icon to edit the text or remove the flag.
@@ -114,11 +114,11 @@ next set: "Save & Continue" and "Defer".
The blue "Save and Continue" button does two things. First, it saves all changes made (i.e., all moved, merged, or
deleted senses), updating the words in the database. Second, it saves any unmerged words as non-duplicates.
-!!! tip
+!!! tip "Tip"
Are the potential duplicates not duplicates? Just click Save & Continue to tell The Combine not to show you that set again.
-!!! note
+!!! note "Note"
If one of the words in an intentionally unmerged set is edited (e.g., in Review Entries), then the set may appear again as potential duplicates.
@@ -141,7 +141,7 @@ information will appear in the Merge Duplicate sense cards as follows:
category (e.g., noun or verb). Hover your cursor over the hexagon to see the specific grammatical category (e.g.,
proper noun or transitive verb).
-![Merge Duplicates sense with definitions and part of speech](images/mergeSenseDefinitionsPartOfSpeech.png){ .center }
+![Merge Duplicates sense with definitions and part of speech](images/mergeSenseDefinitionsPartOfSpeech.png){.center}
#### Protected Entries and Senses
@@ -174,13 +174,13 @@ character, its Unicode "U+" value, the number of times it occurs in entry vernac
Click on a character tile to open a panel for that character.
-!!! tip
+!!! tip "Tip"
You may have to scroll to see the panel. If your window is wide enough, there will be a blank margin on the
right; the panel will be at the top of this. If your window is narrow, tiles fill all the way to the right side of the
window; the panel will be at the bottom, below all the tiles.
-![Character Inventory character panel](images/characterInventoryPanel.png){ .center }
+![Character Inventory character panel](images/characterInventoryPanel.png){.center}
The middle of the panel shows up to 5 example vernacular forms in which the character occurs, highlighting the character
in each occurrence.
@@ -193,6 +193,6 @@ until you click the SAVE button at the bottom of the page.)
At the bottom of the panel is a Find-and-Replace tool. If _every_ occurrence of the character should be replaced with
something else, type the replacement character or string in the "Replace with" box and click the APPLY button.
-!!! important
+!!! warning "Important"
The find-and-replace operation makes changes to entries, not to the character inventory. It **cannot be undone!**
diff --git a/docs/user_guide/docs/index.es.md b/docs/user_guide/docs/index.es.md
new file mode 100644
index 0000000000..f6d8ecc06f
--- /dev/null
+++ b/docs/user_guide/docs/index.es.md
@@ -0,0 +1,18 @@
+# Resumen
+
+The Combine es una herramienta de apoyo a Talleres
+de[recopilación rápida de palabras](https://www.sil.org/dictionaries-lexicography/rapid-word-collection-methodology)
+(RWC) y la limpieza de datos posterior al taller.
+
+## Primeros pasos
+
+Consulte nuestra página [Cuenta](account.md) para saber cómo crear y gestionar una cuenta.
+
+Consulte nuestra página de [Proyectos](project.md) para saber cómo crear y gestionar un proyecto.
+
+## Recopilación y organización de datos
+
+Consulte nuestra página de [introducción de](dataEntry.md) datos para saber cómo recopilarlos.
+
+Consulte nuestra página de [limpieza de datos](goals.md) para conocer las herramientas para editar y organizar los datos
+recopilados.
diff --git a/docs/user_guide/docs/en/index.md b/docs/user_guide/docs/index.md
similarity index 100%
rename from docs/user_guide/docs/en/index.md
rename to docs/user_guide/docs/index.md
diff --git a/docs/user_guide/docs/project.es.md b/docs/user_guide/docs/project.es.md
new file mode 100644
index 0000000000..8d9f2a82e1
--- /dev/null
+++ b/docs/user_guide/docs/project.es.md
@@ -0,0 +1,190 @@
+# Proyectos
+
+Un proyecto es para una sola lengua vernácula.
+
+## Crear un proyecto
+
+Al crear un proyecto, tiene la opción de empezar con un proyecto vacío o de importar los datos léxicos existentes.
+
+![Crear proyecto - Tzotzil](../images/projectCreateTzotzil.png){.center}
+
+### Importar los datos existentes
+
+Si tiene datos léxicos en un archivo [LIFT](https://software.sil.org/lifttools) (probablemente exportado de The Combine,
+[WeSay](https://software.sil.org/wesay), [FLEx](https://software.sil.org/fieldworks) o
+[Lexique Pro](https://software.sil.org/lexiquepro)), puede hacer clic en el botón BROWSE junto a "¿Cargar los datos
+existentes?" para importar los datos a su proyecto.
+
+Si decide no importar datos durante la creación del proyecto, podrá hacerlo más adelante (véase [más abajo](#import)).
+
+### Idioma vernáculo
+
+La _lengua vernácula_ es la lengua para la que se recogen palabras. Suele tratarse de una lengua o dialecto local,
+indígena minoritario, autóctono, patrimonio o lengua o dialecto en peligro de extinción. Una vez creado un proyecto, la
+lengua vernácula no puede cambiarse.
+
+Si selecciona un archivo LIFT para importar durante la creación del proyecto, aparecerá un menú desplegable que le
+permitirá elegir la lengua vernácula del proyecto entre todas las lenguas de los archivos LDML de la importación.
+
+### Idioma de análisis
+
+La _lengua de análisis_ es la lengua primaria a la que se traduce la lengua vernácula. Ésta suele ser una lengua
+regional, nacional, oficial o mayoritaria del lugar donde se habla la lengua vernácula. Análisis adicionales pueden
+añadirse tras la creación del proyecto (véase [más abajo](#project-languages)).
+
+Si selecciona un archivo LIFT para importarlo durante la creación del proyecto, cada idioma utilizado en una definición
+o glosa se añadirá automáticamente al proyecto como lengua de análisis.
+
+## Administrar un proyecto
+
+Cuando se ha creado o seleccionado un proyecto, éste se convierte en el proyecto activo-deberá ver un icono de engranaje
+y/o el nombre del proyecto en el centro de la barra de aplicaciones, en la parte superior de The Combine. Al hacer clic
+en el icono de engranaje o en el nombre del proyecto, aparece Proyecto Ajustes para gestionar el proyecto. Los
+siguientes ajustes están disponibles para los usuarios del proyecto con permisos suficientes.
+
+### Configuración básica
+
+![Configuración básico](../images/projectSettings1Basic.png){width=750 .center}
+
+#### Nombre del Proyecto
+
+Se recomienda un nombre distintivo y descriptivo. El nombre del proyecto forma parte del nombre del archivo cuando usted
+[exporte](#export) su proyecto.
+
+#### Autocompletar {#autocomplete}
+
+El ajuste por defecto es Activado: Cuando un usuario está introduciendo la forma vernácula de una nueva entrada en
+Entrada de datos, este ajuste ofrece sugerencias de entradas similares existentes, lo que permite al usuario seleccionar
+una entrada existente y añadir una nueva acepción a esa entrada, en lugar de crear un duplicado (en su mayoría) a algo
+introducido previamente. Consulte [Introducción de datos](dataEntry.md#new-entry-with-duplicate-vernacular-form) para
+más detalles.
+
+(Esto no afecta a las sugerencias ortográficas para la glosa, ya que dichas sugerencias se basan en un diccionario
+independiente de los datos existentes del proyecto)
+
+#### Archivar el proyecto
+
+Sólo está disponible para el Propietario del proyecto. Archivar un proyecto lo hace inaccesible para todos los usuarios.
+Esto sólo puede ser deshacer por un administrador del sitio. Póngase en contacto con un administrador del sitio si desea
+que el proyecto se elimine por completo de los servidores.
+
+### Idiomas del proyecto {#project-languages}
+
+![Idiomas](../images/projectSettings2Langs.png){width=750 .center}
+
+![Idiomas del proyecto - Tzotzil](../images/projectLanguagesTzotzil.png){.center}
+
+La _lengua vernácula_ especificada en la creación del proyecto es fija.
+
+Puede haber varios _idiomas de análisis_ asociados al proyecto, pero sólo el primero de la lista se asocia con las
+nuevas entradas de datos.
+
+!!! note "Nota"
+
+ Si el proyecto tiene glosas en varios idiomas, esos idiomas deben añadirse aquí para que todas las glosas aparezcan
+ en [Limpieza de datos](goals.md). Pulse el icono de la lupa para ver todos los códigos de idioma presentes en el proyecto.
+
+El _idioma de los_ dominios semánticos controla el idioma en el que se muestran los títulos y descripciones de los
+dominios semánticos en [Entrada de datos](./dataEntry.md).
+
+### Usuarios del proyecto
+
+![Usuarios](../images/projectSettings3Users.png){width=750 .center}
+
+#### Usuarios actuales
+
+Al lado de cada usuario del proyecto hay un icono con tres puntos verticales. Si usted es el Propietario del proyecto o
+un Administrador, puede hacer clic aquí para abrir un menú de gestión de usuarios con las siguientes opciones:
+
+
+ Eliminar del proyecto
+ Cambiar el papel del proyecto:
+ Cosechadora
+ Editor
+ Administrador
+ Convertir el proyecto en Propietario
+ [sólo disponible para el Propietario que modifica un Administrador]
+
+
+Una _cosechadora_ puede hacer [entrada de datos](./dataEntry.md) pero no [limpieza de datos](./goals.md). En los ajustes
+del proyecto, pueden ver los idiomas del proyecto y el calendario del taller, pero no pueden realizar ningún cambio.
+
+Un _Editor_ tiene permiso para hacer todo lo que puede hacer un _Recolector_, además de
+[Revisar entradas](./goals.md#review-entries), [Combinar duplicados](./goals.md#merge-duplicates) y [Exportar](#export).
+
+Un _Administrador_ tiene permiso para hacer todo lo que puede hacer un _Editor_, así como para modificar la mayoría de
+los ajustes del proyecto y los usuarios.
+
+!!! warning "Importante"
+
+ Sólo hay un propietario por proyecto. Si hace "Propietario del proyecto" a otro usuario, pasará automáticamente de Propietario a
+ Administrador para el proyecto, y ya no podrá archivar el proyecto ni hacer/quitar de Administrador a otros usuarios.
+
+#### Agregar usuarios
+
+Buscar usuarios existentes (muestra todos los usuarios con el término de búsqueda en su nombre, nombre de usuario o
+dirección de correo electrónico), o invitar a nuevos usuarios por dirección de correo electrónico (se añadirán
+automáticamente al proyecto cuando se hagan una cuenta a través de la invitación).
+
+### Importar/Exportar
+
+![Importar/Exportar](../images/projectSettings4Port.png){width=750 .center}
+
+#### Importar {#import}
+
+!!! note "Nota"
+
+ Actualmente, el tamaño máximo de los archivos LIFT admitidos para la importación es de 100 MB.
+
+!!! note "Nota"
+
+ Actualmente, sólo se puede importar un archivo LIFT por proyecto.
+
+#### Exportar {#export}
+
+Tras pulsar el botón Exportar, puede navegar por otras partes del sitio web mientras se preparan los datos para la
+descargar. Aparecerá un icono de descarga en la barra de aplicaciones cuando la exportación esté lista para su descarga.
+El nombre de archivo por defecto es el nombre del proyecto con una marca de tiempo añadida.
+
+!!! warning "Importante"
+
+ Un proyecto que haya alcanzado cientos de MB de tamaño puede tardar varios minutos en exportarse.
+
+### Calendario de talleres {#workshop-schedule}
+
+![Calendario de talleres](../images/projectSettings5Sched.png){width=750 .center}
+
+Sólo está disponible para el Propietario del proyecto, lo que permite fijar un calendario para un taller de recopilación
+rápida de palabras. Haga clic en el primer botón para seleccionar un intervalo de fechas para el taller. Pulse el botón
+central para añadir o eliminar fechas concretas. Pulse el último botón para borrar la programación.
+
+![Calendario de talleres](../images/projectSchedule.png){.center}
+
+## Estadísticas del proyecto
+
+Si usted es el Propietario del proyecto, habrá otro icono junto al icono del engranaje en la App Bar, en la parte
+superior de The Combine. Esto abre las estadísticas sobre las palabras en el proyecto.
+
+![Botón de estadísticas del proyecto](../images/projectStatsButton.png){.center}
+
+En el contexto de estas estadísticas, _palabra_ se refiere a un par acepción-dominio: por ejemplo, una entrada con 3
+acepciones, cada uno con 2 dominios semánticos, se contará como 6 palabras.
+
+### Palabras por usuario
+
+Una tabla con el número de palabras y dominios semánticos distintos para cada usuario del proyecto. Las palabras
+importadas no tienen un usuario y se contabilizarán en una fila "unknownUser".
+
+### Palabras por dominio
+
+Una tabla con el número de palabras de cada dominio semántico.
+
+### Palabras por día
+
+Gráficos lineales que muestran las palabras recogidas durante los días especificados en el
+[Calendario de talleres](#workshop-schedule).
+
+### Progresos del taller
+
+Gráficos lineales que muestran las palabras acumuladas a lo largo de los días del
+[Calendario de talleres](#workshop-schedule), así como las previsiones para el resto del taller.
diff --git a/docs/user_guide/docs/en/project.md b/docs/user_guide/docs/project.md
similarity index 87%
rename from docs/user_guide/docs/en/project.md
rename to docs/user_guide/docs/project.md
index 8d3b4c1b59..03ad7909be 100644
--- a/docs/user_guide/docs/en/project.md
+++ b/docs/user_guide/docs/project.md
@@ -6,7 +6,7 @@ A project is for a single vernacular language.
When creating a project, you have the option to start with an empty project or to import existing lexical data.
-![Create Project - Tzotzil](images/projectCreateTzotzil.png){ .center }
+![Create Project - Tzotzil](images/projectCreateTzotzil.png){.center}
### Import Existing Data
@@ -43,14 +43,14 @@ Settings for managing the project. The following settings are available for proj
### Basic Settings
-![BasicSettings](images/projectSettings1Basic.png){ width=750 .center }
+![BasicSettings](images/projectSettings1Basic.png){width=750 .center}
#### Project Name
A distinguishing and descriptive name is recommended. The project name is part of the filename when you
[export](#export) your project.
-#### Autocomplete
+#### Autocomplete {#autocomplete}
The default setting is On: When a user is entering the vernacular form of a new entry in Data Entry, this setting gives
suggestions of similar existing entries, allowing the user to select an existing entry and add a new sense to that
@@ -66,18 +66,18 @@ This is only available to the project Owner. Archiving a project makes it inacce
undone by a site administrator. Please contact a site administrator if you wish for the project to be entirely deleted
from the servers.
-### Project Languages
+### Project Languages {#project-languages}
-![Languages](images/projectSettings2Langs.png){ width=750 .center }
+![Languages](images/projectSettings2Langs.png){width=750 .center}
-![Project Languages - Tzotzil](images/projectLanguagesTzotzil.png){ .center }
+![Project Languages - Tzotzil](images/projectLanguagesTzotzil.png){.center}
The _vernacular language_ specified at project creation is fixed.
There may be multiple _analysis languages_ associated with the project, but only the top one in the list is associated
with new data entries.
-!!! note
+!!! note "Note"
If the project has glosses in multiple languages, those languages must be added here for all the glosses to show up
in [Data Cleanup](goals.md). Click the magnifying glass icon to see all language codes present in the project.
@@ -87,7 +87,7 @@ The _semantic domains language_ controls the language in which semantic domain t
### Project Users
-![Users](images/projectSettings3Users.png){ width=750 .center }
+![Users](images/projectSettings3Users.png){width=750 .center}
#### Current Users
@@ -100,19 +100,20 @@ click this to open a user management menu with the following options:
Harvester
Editor
Administrator
- Make project Owner [only available to the Owner modifying an Administrator]
+ Make project Owner
+ [only available to the Owner modifying an Administrator]
A _Harvester_ can do [Data Entry](./dataEntry.md) but not [Data Cleanup](./goals.md). In project settings, they can see
the project languages and workshop schedule, but cannot make any changes.
An _Editor_ has permission to do everything that a _Harvester_ can do, as well as
-[Review Entries](./goals.md#review-entries), [Merge Duplicates](./goals.md#review-entries), and [Export](#export).
+[Review Entries](./goals.md#review-entries), [Merge Duplicates](./goals.md#merge-duplicates), and [Export](#export).
An _Administrator_ has permission to do everything that an _Editor_ can do, as well as modify most project settings and
users.
-!!! important
+!!! warning "Important"
There is only one Owner per project. If you "Make project Owner" another user, you will automatically change from Owner to
Administrator for the project, and you will no longer be able to archive the project or make/remove Administrator on other users.
@@ -125,44 +126,44 @@ invitation).
### Import/Export
-![Import/Export](images/projectSettings4Port.png){ width=750 .center }
+![Import/Export](images/projectSettings4Port.png){width=750 .center}
-#### Import
+#### Import {#import}
-!!! note
+!!! note "Note"
Currently, the maximum size of LIFT files supported for import is 100MB.
-!!! note
+!!! note "Note"
Currently, only one LIFT file can be imported per project.
-#### Export
+#### Export {#export}
After clicking the Export button, you can navigate to other parts of the website while the data is being prepared for
download. A download icon will appear in the App Bar when the export is ready for download. The default filename is the
-[project name](#project-name) with a timestamp affixed.
+project name with a timestamp affixed.
-!!! important
+!!! warning "Important"
A project that has reached hundreds of MB in size may take multiple minutes to export.
-### Workshop Schedule
+### Workshop Schedule {#workshop-schedule}
-![Workshop Schedule](images/projectSettings5Sched.png){ width=750 .center }
+![Workshop Schedule](images/projectSettings5Sched.png){width=750 .center}
This is only available to the project Owner, allowing a schedule to be set for a Rapid Word Collection workshop. Click
the first button to select a date range for the workshop. Click the middle button to add or remove specific dates. Click
the last button to clear the schedule.
-![Workshop Schedule](images/projectSchedule.png){ .center }
+![Workshop Schedule](images/projectSchedule.png){.center}
## Project Statistics
If you are the project Owner, there will be another icon alongside the gear icon in App Bar at the top of The Combine.
This opens statistics about words in the project.
-![Project Statistics Button](images/projectStatsButton.png){ .center }
+![Project Statistics Button](images/projectStatsButton.png){.center}
In the context of these statistics, _word_ refers to a sense-domain pair: e.g., an entry with 3 senses, each with 2
semantic domains, will be counted as 6 words.
diff --git a/docs/user_guide/config/en/mkdocs.yml b/docs/user_guide/mkdocs.yml
similarity index 69%
rename from docs/user_guide/config/en/mkdocs.yml
rename to docs/user_guide/mkdocs.yml
index a49e962edf..796c951aae 100644
--- a/docs/user_guide/config/en/mkdocs.yml
+++ b/docs/user_guide/mkdocs.yml
@@ -1,14 +1,13 @@
strict: true
theme:
name: material
- custom_dir: "../../default/"
+ custom_dir: "assets/"
palette:
primary: blue
icon:
logo: material/barley
repo: fontawesome/brands/github
favicon: images/favicon.ico
- language: en
# Disable pulling Google fonts from the Internet, to support offline networks.
font: false
features:
@@ -16,16 +15,34 @@ theme:
- search.share
- search.suggest
site_name: The Combine
-docs_dir: "../../docs/en"
-site_dir: "../../site/en"
+site_description: A Web App for Harvesting Words Together
+docs_dir: "docs/"
+site_dir: "site/"
use_directory_urls: false
repo_url: https://github.com/sillsdev/TheCombine
edit_uri: edit/master/docs/user_guide/docs/
repo_name: sillsdev/TheCombine
copyright: © 2019-2023 SIL International
plugins:
+ - i18n:
+ docs_structure: suffix
+ languages:
+ - locale: en
+ default: true
+ name: English
+ build: true
+ - locale: es
+ name: Español
+ build: true
+ nav_translations:
+ Overview: Resumen
+ Account: Cuenta
+ Projects: Proyectos
+ Data Entry: Entrada de datos
+ Data Cleanup: Depuración de datos
+ Admin: Administración
- search:
- lang: en
+ lang: [en, es]
# This plugin is used to validate URLs (including some anchors).
# Uncomment to build the docs with this validation enabled.
# This is commented out by default because this requires network I/O to validate URLs, so it is
@@ -38,12 +55,19 @@ markdown_extensions:
extra_css:
- static/stylesheets/extra.css
extra:
+ alternate:
+ - name: English
+ link: /
+ lang: en
+ - name: Español
+ link: /es/
+ lang: es
social:
- icon: fontawesome/solid/globe
link: https://www.sil.org/
- icon: fontawesome/brands/github
link: https://github.com/sillsdev
- - icon: fontawesome/brands/twitter
+ - icon: fontawesome/brands/x-twitter
link: https://twitter.com/silintl
- icon: fontawesome/brands/facebook
link: https://www.facebook.com/SIL.International/
@@ -62,3 +86,6 @@ nav:
- Backend: licenses/backend_licenses.txt
- Fonts: licenses/OFL.txt
- Frontend: licenses/frontend_licenses.txt
+validation:
+ links:
+ not_found: info
diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template
index 7d05c42e33..745f4bda63 100644
--- a/nginx/templates/default.conf.template
+++ b/nginx/templates/default.conf.template
@@ -66,7 +66,7 @@ server {
# User Guide static files.
location /docs {
- alias /usr/share/nginx/user_guide/en;
+ alias /usr/share/nginx/user_guide;
index index.html index.htm;
expires 12h;
add_header Cache-Control "public, no-transform";
diff --git a/package-lock.json b/package-lock.json
index 01d31a3768..38ac7c3cd8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -140,16 +140,81 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
- "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "version": "7.22.13",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+ "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dependencies": {
- "@babel/highlight": "^7.18.6"
+ "@babel/highlight": "^7.22.13",
+ "chalk": "^2.4.2"
},
"engines": {
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/code-frame/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+ },
+ "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/@babel/compat-data": {
"version": "7.18.8",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz",
@@ -258,13 +323,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.20.14",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz",
- "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==",
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
+ "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.20.7",
+ "@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
},
"engines": {
@@ -411,9 +477,9 @@
}
},
"node_modules/@babel/helper-environment-visitor": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
- "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -432,25 +498,25 @@
}
},
"node_modules/@babel/helper-function-name": {
- "version": "7.19.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
- "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"dev": true,
"dependencies": {
- "@babel/template": "^7.18.10",
- "@babel/types": "^7.19.0"
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
@@ -579,29 +645,29 @@
}
},
"node_modules/@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "version": "7.22.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+ "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.19.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
- "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
+ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.19.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
- "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"engines": {
"node": ">=6.9.0"
}
@@ -645,12 +711,12 @@
}
},
"node_modules/@babel/highlight": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
- "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
+ "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "chalk": "^2.0.0",
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "chalk": "^2.4.2",
"js-tokens": "^4.0.0"
},
"engines": {
@@ -722,9 +788,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
- "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
+ "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -2161,33 +2227,33 @@
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
},
"node_modules/@babel/template": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
- "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
+ "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"dev": true,
"dependencies": {
- "@babel/code-frame": "^7.18.6",
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7"
+ "@babel/code-frame": "^7.22.13",
+ "@babel/parser": "^7.22.15",
+ "@babel/types": "^7.22.15"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.20.13",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz",
- "integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.7",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.19.0",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.20.13",
- "@babel/types": "^7.20.7",
+ "version": "7.23.2",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
+ "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/generator": "^7.23.0",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.23.0",
+ "@babel/types": "^7.23.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
@@ -2228,12 +2294,12 @@
"dev": true
},
"node_modules/@babel/types": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
- "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
+ "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dependencies": {
- "@babel/helper-string-parser": "^7.19.4",
- "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0"
},
"engines": {
diff --git a/package.json b/package.json
index 291ee9aca6..528b21a57a 100644
--- a/package.json
+++ b/package.json
@@ -17,9 +17,9 @@
"fmt-frontend-check": "prettier --check \"./{.github,.vscode,certmgr,deploy,docs,public,scripts,src}/**/*.{js,jsx,ts,tsx,json,md,yml,yaml}\"",
"frontend": "npm run set-release && react-scripts start",
"gen-backend-coverage-report": "tsc scripts/genBackendCoverageReport.ts && node scripts/genBackendCoverageReport.js",
- "license-report-backend": " dotnet-project-licenses --include-transitive --input ./Backend/BackendFramework.csproj --output --outfile docs/user_guide/default/licenses/backend_licenses.txt",
+ "license-report-backend": " dotnet-project-licenses --include-transitive --input ./Backend/BackendFramework.csproj --output --outfile docs/user_guide/assets/licenses/backend_licenses.txt",
"license-summary-backend": "dotnet-project-licenses --include-transitive --input ./Backend/BackendFramework.csproj",
- "license-report-frontend": " npm install && license-checker-rseidelsohn --production --excludePrivatePackages --plainVertical --out docs/user_guide/default/licenses/frontend_licenses.txt",
+ "license-report-frontend": " npm install && license-checker-rseidelsohn --production --excludePrivatePackages --plainVertical --out docs/user_guide/assets/licenses/frontend_licenses.txt",
"license-summary-frontend": "npm install && license-checker-rseidelsohn --production --excludePrivatePackages --summary",
"lint": " eslint --max-warnings=0 --ext js,ts,tsx,jsx src",
"lint:fix-layout": "eslint --max-warnings=0 --ext js,ts,tsx,jsx src --fix --fix-type layout,suggestion",
diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json
index a8b762eaaf..f8ca935052 100644
--- a/public/locales/es/translation.json
+++ b/public/locales/es/translation.json
@@ -15,7 +15,8 @@
},
"treeView": {
"findDomain": "Buscar un dominio",
- "domainNotFound": "Dominio no encontrado"
+ "domainNotFound": "Dominio no encontrado",
+ "returnToTop": "Volver a la parte superior del árbol de dominios."
},
"addWords": {
"selectEntry": "Seleccionar una entrada",
@@ -40,7 +41,9 @@
"statistics": "Estadísticas de datos",
"projectSettings": "Configuración del proyecto"
},
- "invite": {},
+ "invite": {
+ "invalidInvitationURL": "El enlace de invitación al proyecto no es válido."
+ },
"login": {
"title": "Iniciar Sesión",
"name": "Su nombre",
@@ -55,10 +58,9 @@
"signUpNew": "Registrar un nuevo usuario",
"signUpSuccess": "Usuario creado",
"signUpFailed": "Error al registrarse",
- "usernameInvalid": "Este nombre de usuario es demasiado corto o ya está en uso.",
"usernameRequirements": "El nombre de usuario debe tener 3 caracteres o más.",
- "emailTaken": "Este correo electrónico ya está en uso.",
- "networkError": "Error de red",
+ "usernameTaken": "Nombre de usuario no disponible.",
+ "emailTaken": "Correo electrónico no disponible.",
"loggingIn": "Iniciando sesión...",
"required": "Requerido",
"confirmPassword": "Confirmar contraseña",
@@ -67,12 +69,14 @@
"emailError": "Por favor, ingrese un correo electrónico válida."
},
"passwordReset": {
+ "invalidURL": "El enlace para restablecer la contraseña no es válido.",
"emailOrUsername": "Correo electrónico o nombre de usuario",
"resetRequestTitle": "Solicitud de restablecimiento de contraseña",
"resetTitle": "Restablecer la contraseña",
"resetRequestInstructions": "Le enviaremos a su correo electrónico un enlace para restablecer su cuenta una sola vez.",
"submit": "Enviar",
"resetFail": "Error al restablecer la contraseña",
+ "resetDone": "Si ha introducido correctamente su dirección de correo electrónico o su nombre de usuario, se le habrá enviado un enlace para restablecer la contraseña.",
"backToLogin": "Volver al inicio de sesión"
},
"userMenu": {
@@ -88,6 +92,7 @@
"uploadFormat": "El archivo debe ser una carpeta comprimida que contenga un proyecto <1>Lexicon Interchange FormaT (LIFT)1>.",
"create": "Crear Proyecto",
"fileSelected": "Archivo seleccionado",
+ "language": "La información lingüística del proyecto se extraerá automáticamente de los datos cargados.",
"languageOptionOther": "otro",
"languageSelect": "Seleccionar de idiomas en el archivo:",
"success": "¡Proyecto creado!"
@@ -95,6 +100,8 @@
"selectProject": { "title": "Seleccionar Proyecto" },
"siteSettings": {
"projectList": "Proyectos",
+ "projectRoles": "Roles del proyecto",
+ "addProjectUsers": "Agregar usuarios del proyecto",
"archiveProjectText": "Este proyecto no será accesible para ningún usuario.",
"restoreProjectText": "Alguien probablemente tuvo una buena razón para archivar este proyecto. ¿Seguro que quieres restaurarlo?",
"userList": "Usuarios",
@@ -126,6 +133,15 @@
"exportInProgress": "Exportación en curso"
},
"projectSettings": {
+ "project": "Proyecto:",
+ "tab": {
+ "basic": "Configuración básica",
+ "export": "Exportar",
+ "importExport": "Importar/Exportar",
+ "languages": "Idiomas",
+ "schedule": "Programar",
+ "users": "Usuarios"
+ },
"language": {
"header": "Idioma",
"vernacular": "Vernáculo",
@@ -134,14 +150,16 @@
"analysisLanguage": "Idioma de análisis",
"semanticDomains": "Dominios semánticos",
"semanticDomainsDefault": "(Por defecto, el idioma del navegador)",
- "languages": "Idiomas",
+ "languages": "Idiomas del proyecto",
"bcp47": "Código BCP 47",
"name": "Nombre",
"font": "Fuente",
+ "changeName": "Cambiar nombre",
"addAnalysisLanguage": "Agregar un idioma de análisis alternativo",
"makeDefaultAnalysisLanguage": "Haga que este sea el idioma de análisis por defecto.",
"deleteAnalysisLanguage": "Eliminar este idioma de análisis.",
"getGlossLanguages": "Encuentre todos los códigos de idioma utilizados en los datos actuales.",
+ "updateVernacularLanguageNameFailed": "Error al actualizar el nombre del idioma vernáculo",
"addAnalysisLanguageFailed": "No se ha podido añadir el lenguaje de análisis",
"makeDefaultAnalysisLanguageFailed": "No se ha podido establecer el lenguaje de análisis por defecto",
"deleteAnalysisLanguageFailed": "Fallo en la eliminación del lenguaje de análisis",
@@ -154,16 +172,24 @@
"addUser": "Agregar usuario"
},
"roles": {
+ "harvester": "Cosechador",
"editor": "Editor",
"administrator": "Administrador",
"owner": "Propietario"
},
"userManagement": {
+ "removeFromProject": "Eliminar del proyecto",
+ "changeProjectRole": "Cambiar rol del proyecto:",
+ "makeOwner": "Hacer propietario del proyecto",
"removeUserWarning": "¿Eliminar a este usuario del proyecto?",
+ "makeHarvesterWarning": "¿Hacer a este usuario un cosechador de proyecto?",
+ "makeEditorWarning": "¿Hacer de este usuario un editor de proyecto?",
"makeAdminWarning": "¿Hacer de este usuario un Administrador del proyecto?",
"makeOwnerWarning": "¿Transferir la propiedad del proyecto a este usuario?",
"userRemovedToastSuccess": "Usuario eliminado del proyecto.",
"userRemovedToastFailure": "Error al eliminar el usuario.",
+ "userRoleUpdateToastSuccess": "Rol de usuario actualizado con éxito.",
+ "userRoleUpdateToastFailure": "Error al actualizar el rol de usuario.",
"makeOwnerToastSuccess": "¡Transferencia de la responsabilidad del proyecto!",
"makeOwnerToastFailure": "No se ha transferido la responsabilidad del proyecto.",
"manageUser": "Administrar usuario",
@@ -227,6 +253,7 @@
},
"createStrWordInv": { "title": "Crear inventario de palabras estructurales" },
"handleFlags": { "title": "Manejar banderas" },
+ "reviewDeferredDups": { "title": "Revisar duplicados aplazados" },
"spellCheckGloss": { "title": "Corregir la ortografía del glosario" },
"validateChars": { "title": "Validar caracteres" },
"validateStrWords": { "title": "Validar los caracteres estructurales" },
@@ -237,6 +264,7 @@
"noDefinition": "No hay definiciones",
"noGloss": "No hay entrada de glosas",
"noDomain": "Ningún dominio seleccionado",
+ "duplicateDomain": "Esta acepción ya tiene el dominio {{ val }}.",
"noNote": "Sin nota",
"deleteWordWarning": "¡Esta palabra se eliminará permanentemente!",
"deleteDisabled": "Se importó con datos que The Combine no maneja, por lo que no se puede eliminar.",
@@ -302,6 +330,7 @@
"replaceAll": "Reemplazar todas las ocurrencias de: {{ val }}",
"replaceAllWith": "Reemplazar con: {{ val }}",
"findAndReplace": "Buscar y Reemplazar",
+ "findAndReplaceError": "Error al procesar: encontrar ( {{ val1 }} ), reemplazar ( {{ val2 }})",
"apply": "aplicar"
},
"sampleWords": {
@@ -329,7 +358,7 @@
"dups": "Arrastra las palabras duplicadas aquí",
"sense": "Arrastra una nueva acepción aquí",
"saveAndContinue": "Guardar los cambios y cargar un nuevo conjunto de palabras",
- "skip": "Descartar los cambios y cargar un nuevo conjunto de palabras",
+ "defer": "Descartar los cambios y cargar un nuevo conjunto de palabras",
"list": "Arrastre esta palabra hacia la derecha para comenzar a combinarla con otras palabras",
"noDups": "No hay nada que combinar.",
"delete": "Borrar la acepción",
@@ -365,6 +394,7 @@
"cancel": "Cancelar",
"clearText": "Borrar el texto",
"confirm": "Confirmar",
+ "defer": "Aplazar",
"delete": "Eliminar",
"deletePermanently": "¿Eliminar permanentemente?",
"done": "Terminado",
@@ -378,7 +408,6 @@
"restore": "Restaurar",
"save": "Guardar",
"saveAndContinue": "Guardar y Continuar",
- "skip": "Saltar",
"undecided": "No decidido",
"upload": "Subir"
},
@@ -415,12 +444,20 @@
"Adposition": "Adposición",
"Adverb": "Adverbio",
"Classifier": "Clasificador",
+ "Connective": "Conectivo",
"Determiner": "Determinante",
+ "ExistentialMarker": "Marcador existencial",
+ "Expletive": "Expletivo",
"Interjection": "Interjección",
"Noun": "Sustantivo",
"Participle": "Participio",
"Particle": "Partícula",
- "Verb": "Verbo"
+ "Prenoun": "Prefijos adjetivales",
+ "Preverb": "Prefijos verbales separables",
+ "ProForm": "Proforma",
+ "Verb": "Verbo",
+ "Other": "Otra",
+ "Unspecified": "No especificado"
}
}
}
diff --git a/scripts/add_user_guide.sh b/scripts/add_user_guide.sh
new file mode 100644
index 0000000000..ebab20db3e
--- /dev/null
+++ b/scripts/add_user_guide.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+######################################################
+# Script to add translated User Guide files
+# that have been downloaded from Crowdin.
+######################################################
+
+set -e
+
+usage() {
+ cat <
{props.vernacular}
@@ -33,21 +32,20 @@ export default function ImmutableExistingData(
-
- {props.gloss.def}
-
+ {props.glosses.map((g, i) => (
+
+ {g.def}
+
+ ))}
);
diff --git a/src/components/DataEntry/ExistingDataTable/index.tsx b/src/components/DataEntry/ExistingDataTable/index.tsx
index 6404450829..0888c13f07 100644
--- a/src/components/DataEntry/ExistingDataTable/index.tsx
+++ b/src/components/DataEntry/ExistingDataTable/index.tsx
@@ -35,11 +35,12 @@ export default function ExistingDataTable(
const list = (): ReactElement => (
- {props.domainWords.map((domainWord) => (
+ {props.domainWords.map((w, i) => (
))}
diff --git a/src/components/DataEntry/ExistingDataTable/tests/ImmutableExistingData.test.tsx b/src/components/DataEntry/ExistingDataTable/tests/ImmutableExistingData.test.tsx
index 8e0703af83..a4e4b93f13 100644
--- a/src/components/DataEntry/ExistingDataTable/tests/ImmutableExistingData.test.tsx
+++ b/src/components/DataEntry/ExistingDataTable/tests/ImmutableExistingData.test.tsx
@@ -1,13 +1,17 @@
-import renderer from "react-test-renderer";
+import { act, create } from "react-test-renderer";
import ImmutableExistingData from "components/DataEntry/ExistingDataTable/ImmutableExistingData";
import { newGloss } from "types/word";
describe("ImmutableExistingData", () => {
- it("render without crashing", () => {
- renderer.act(() => {
- renderer.create(
-
+ it("renders", async () => {
+ await act(async () => {
+ create(
+
);
});
});
diff --git a/src/components/DataEntry/index.tsx b/src/components/DataEntry/index.tsx
index 8bc04848ec..6b37c8e8d0 100644
--- a/src/components/DataEntry/index.tsx
+++ b/src/components/DataEntry/index.tsx
@@ -13,10 +13,7 @@ import AppBar from "components/AppBar/AppBarComponent";
import DataEntryHeader from "components/DataEntry/DataEntryHeader";
import DataEntryTable from "components/DataEntry/DataEntryTable";
import ExistingDataTable from "components/DataEntry/ExistingDataTable";
-import {
- filterWordsByDomain,
- sortDomainWordsByVern,
-} from "components/DataEntry/utilities";
+import { filterWordsByDomain } from "components/DataEntry/utilities";
import TreeView from "components/TreeView";
import {
closeTreeAction,
@@ -46,6 +43,10 @@ const paperStyle = {
export default function DataEntry(): ReactElement {
const dispatch = useAppDispatch();
+ const analysisLang = useAppSelector(
+ (state: StoreState) =>
+ state.currentProjectState.project.analysisWritingSystems[0].bcp47
+ );
const { currentDomain, open } = useAppSelector(
(state: StoreState) => state.treeViewState
);
@@ -95,10 +96,11 @@ export default function DataEntry(): ReactElement {
}, [domain, questionsVisible, updateHeight, windowWidth]);
const returnControlToCaller = useCallback(async () => {
- const words = filterWordsByDomain(await getFrontierWords(), id);
- setDomainWords(sortDomainWordsByVern(words));
+ setDomainWords(
+ filterWordsByDomain(await getFrontierWords(), id, analysisLang)
+ );
dispatch(closeTreeAction());
- }, [dispatch, id]);
+ }, [analysisLang, dispatch, id]);
return (
diff --git a/src/components/DataEntry/tests/index.test.tsx b/src/components/DataEntry/tests/index.test.tsx
index a1f7f426ff..85a4ec1aff 100644
--- a/src/components/DataEntry/tests/index.test.tsx
+++ b/src/components/DataEntry/tests/index.test.tsx
@@ -8,6 +8,7 @@ import DataEntry, {
smallScreenThreshold,
treeViewDialogId,
} from "components/DataEntry";
+import { defaultState as currentProjectState } from "components/Project/ProjectReduxTypes";
import { openTreeAction } from "components/TreeView/Redux/TreeViewActions";
import {
TreeViewAction,
@@ -97,7 +98,7 @@ async function renderDataEntry(
spyOnUseWindowSize(windowWidth);
await renderer.act(async () => {
testHandle = renderer.create(
-
+
);
diff --git a/src/components/DataEntry/tests/utilities.test.ts b/src/components/DataEntry/tests/utilities.test.ts
index 5a72dcfe21..8b5c63f11f 100644
--- a/src/components/DataEntry/tests/utilities.test.ts
+++ b/src/components/DataEntry/tests/utilities.test.ts
@@ -1,46 +1,64 @@
-import { Status, Word } from "api/models";
+import { Sense, Status, Word } from "api/models";
import {
filterWordsByDomain,
filterWordsWithSenses,
- sortDomainWordsByVern,
} from "components/DataEntry/utilities";
import { newSemanticDomain } from "types/semanticDomain";
import { DomainWord, newSense, simpleWord } from "types/word";
const mockWord = simpleWord("vern", "gloss");
+const domainSense = (accessibility: Status, domainId?: string): Sense => {
+ const semanticDomains = [newSemanticDomain(domainId)];
+ return { ...newSense(), accessibility, semanticDomains };
+};
describe("DataEntryComponent", () => {
describe("filterWordsWithSenses", () => {
it("returns empty Word Array when given empty Word Array.", () => {
- const words: Word[] = [];
- const expectedWords: Word[] = [];
- expect(filterWordsWithSenses(words)).toEqual(expectedWords);
+ expect(filterWordsWithSenses([])).toEqual([]);
});
it("removes words with no Active/Protected sense.", () => {
const words: Word[] = [
- {
- ...mockWord,
- senses: [{ ...newSense(), accessibility: Status.Deleted }],
- },
- {
- ...mockWord,
- senses: [{ ...newSense(), accessibility: Status.Duplicate }],
- },
+ { ...mockWord, senses: [domainSense(Status.Deleted)] },
+ { ...mockWord, senses: [domainSense(Status.Duplicate)] },
];
expect(filterWordsWithSenses(words)).toHaveLength(0);
});
it("keeps words with an Active/Protected sense.", () => {
const words: Word[] = [
- mockWord,
- {
- ...mockWord,
- senses: [{ ...newSense(), accessibility: Status.Protected }],
- },
+ { ...mockWord, senses: [domainSense(Status.Active)] },
+ { ...mockWord, senses: [domainSense(Status.Protected)] },
];
expect(filterWordsWithSenses(words)).toHaveLength(2);
});
+
+ it("removes words with inactive sense even in specified domain.", () => {
+ const domId = "domain-id";
+ const words: Word[] = [
+ { ...mockWord, senses: [domainSense(Status.Deleted, domId)] },
+ { ...mockWord, senses: [domainSense(Status.Duplicate, domId)] },
+ ];
+ expect(filterWordsWithSenses(words, domId)).toHaveLength(0);
+ });
+
+ it("removes words with sense in wrong domain.", () => {
+ const words: Word[] = [
+ { ...mockWord, senses: [domainSense(Status.Active, "one wrong")] },
+ { ...mockWord, senses: [domainSense(Status.Protected, "other wrong")] },
+ ];
+ expect(filterWordsWithSenses(words, "right one")).toHaveLength(0);
+ });
+
+ it("keeps words with an Active/Protected sense in specified domain.", () => {
+ const domId = "domain-id";
+ const words: Word[] = [
+ { ...mockWord, senses: [domainSense(Status.Active, domId)] },
+ { ...mockWord, senses: [domainSense(Status.Protected, domId)] },
+ ];
+ expect(filterWordsWithSenses(words, domId)).toHaveLength(2);
+ });
});
describe("filterWordsByDomain", () => {
@@ -83,18 +101,4 @@ describe("DataEntryComponent", () => {
).toStrictEqual([new DomainWord(expectedWord, senseIndex)]);
});
});
-
- describe("sortDomainWordByVern", () => {
- it("sorts words alphabetically.", () => {
- const words = [mockWord, mockWord, mockWord].map(
- (w) => new DomainWord(w)
- );
- words[0].vernacular = "Always";
- words[1].vernacular = "Be";
- words[2].vernacular = "?character";
-
- const expectedList: DomainWord[] = [words[2], words[0], words[1]];
- expect(sortDomainWordsByVern([...words])).toStrictEqual(expectedList);
- });
- });
});
diff --git a/src/components/DataEntry/utilities.ts b/src/components/DataEntry/utilities.ts
index b9d438a0e2..1d641ae770 100644
--- a/src/components/DataEntry/utilities.ts
+++ b/src/components/DataEntry/utilities.ts
@@ -1,39 +1,51 @@
-import { Status, Word } from "api/models";
+import { Sense, Status, Word } from "api/models";
import { DomainWord } from "types/word";
-/** Filter out words that do not have at least 1 active sense */
-export function filterWordsWithSenses(words: Word[]): Word[] {
+/** Checks whether a sense is active
+ * (and in the specified domain if domainId is provided). */
+function isActiveInDomain(sense: Sense, domainId?: string): boolean {
+ return (
+ (!domainId || !!sense.semanticDomains.find((d) => d.id === domainId)) &&
+ // The undefined is for Statuses created before .accessibility was required.
+ [Status.Active, Status.Protected, undefined].includes(sense.accessibility)
+ );
+}
+
+/** Filter out words that do not have at least 1 active sense
+ * (and in the specified domain if domainId is provided). */
+export function filterWordsWithSenses(
+ words: Word[],
+ domainId?: string
+): Word[] {
return words.filter((w) =>
- w.senses.find((s) =>
- [Status.Active, Status.Protected].includes(s.accessibility)
- )
+ w.senses.find((s) => isActiveInDomain(s, domainId))
);
}
+/** Filter out sense's glosses with empty def
+ * (and if lang is specified, put glosses in that lang first). */
+function filterGlosses(sense: Sense, lang?: string): Sense {
+ const glosses = sense.glosses.filter((g) => g.def.trim());
+ if (lang) {
+ glosses.sort((a, b) => +(b.language === lang) - +(a.language === lang));
+ }
+ return { ...sense, glosses };
+}
+
export function filterWordsByDomain(
words: Word[],
- domainId: string
+ domainId: string,
+ lang?: string
): DomainWord[] {
const domainWords: DomainWord[] = [];
- for (const currentWord of words) {
- const senses = currentWord.senses.filter((s) =>
- // The undefined is for Statuses created before .accessibility was required in the frontend.
- [Status.Active, Status.Protected, undefined].includes(s.accessibility)
+ const wordsInDomain = filterWordsWithSenses(words, domainId);
+ wordsInDomain.sort((a, b) => a.vernacular.localeCompare(b.vernacular));
+ for (const w of wordsInDomain) {
+ domainWords.push(
+ ...w.senses
+ .filter((s) => isActiveInDomain(s, domainId))
+ .map((s) => new DomainWord({ ...w, senses: [filterGlosses(s, lang)] }))
);
- for (const sense of senses) {
- if (sense.semanticDomains.map((dom) => dom.id).includes(domainId)) {
- // Only the first gloss is shown, and no definitions.
- domainWords.push(new DomainWord({ ...currentWord, senses: [sense] }));
- }
- }
}
return domainWords;
}
-
-export function sortDomainWordsByVern(words: DomainWord[]): DomainWord[] {
- return words.sort(
- (a, b) =>
- a.vernacular.localeCompare(b.vernacular) ||
- a.gloss.def.localeCompare(b.gloss.def)
- );
-}
diff --git a/src/types/word.ts b/src/types/word.ts
index 68e0230423..c82dd30191 100644
--- a/src/types/word.ts
+++ b/src/types/word.ts
@@ -78,14 +78,14 @@ export class DomainWord {
wordGuid: string;
vernacular: string;
senseGuid: string;
- gloss: Gloss;
+ glosses: Gloss[];
- constructor(word: Word, senseIndex = 0, glossIndex = 0) {
+ constructor(word: Word, senseIndex = 0) {
const sense = word.senses[senseIndex] ?? newSense();
- this.gloss = sense.glosses[glossIndex] ?? newGloss();
this.wordGuid = word.guid;
this.vernacular = word.vernacular;
this.senseGuid = sense.guid;
+ this.glosses = sense.glosses;
}
}
diff --git a/tox.ini b/tox.ini
index 3bc96cea0f..22c67c6e3a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -47,21 +47,21 @@ commands =
[testenv:user-guide]
deps =
-r{toxinidir}/dev-requirements.txt
-changedir = {toxinidir}/docs/user_guide/config/en
+changedir = {toxinidir}/docs/user_guide/
commands =
mkdocs build
[testenv:user-guide-serve]
deps =
-r{toxinidir}/dev-requirements.txt
-changedir = {toxinidir}/docs/user_guide/config/en
+changedir = {toxinidir}/docs/user_guide/
commands =
mkdocs serve
[testenv:user-guide-github-pages]
deps =
-r{toxinidir}/dev-requirements.txt
-changedir = {toxinidir}/docs/user_guide/config/en
+changedir = {toxinidir}/docs/user_guide/
commands =
# See: https://squidfunk.github.io/mkdocs-material/publishing-your-site/
mkdocs gh-deploy --force