From 720b4cfc91e32d9cb8d97549d835afd8660165f3 Mon Sep 17 00:00:00 2001 From: Santiago Balladares <744091+santiagoballadares@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:27:56 +0200 Subject: [PATCH] add crowdin translations - Add Crowdin config file - Cleanup Spanish translations - Fix some keys sorting --- .gitignore | 4 + README.md | 44 +++++++ crowdin.yml | 8 ++ .../source/kotti-i18n/locales/de-DE.ts | 7 +- .../source/kotti-i18n/locales/en-US.ts | 6 +- .../source/kotti-i18n/locales/es-ES.ts | 46 +++---- .../source/kotti-i18n/locales/fr-FR.ts | 6 +- .../source/kotti-i18n/locales/ja-JP.ts | 7 +- packages/kotti-ui/source/locales/input.json | 115 ++++++++++++++++++ 9 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 crowdin.yml create mode 100644 packages/kotti-ui/source/locales/input.json diff --git a/.gitignore b/.gitignore index 74fc34d2db..e793e7df50 100755 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ vite.config.ts.timestamp* # package.json tarball script /tarballs + +# crowdin (ignore all but the source file) +packages/kotti-ui/source/locales/*.json +!packages/kotti-ui/source/locales/input.json diff --git a/README.md b/README.md index 3dc509d9a0..05dbc42eb0 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,50 @@ There are two workflows to help with rebasing pull requests: Adding the `autorebase:opt-in` label to any pull request will automatically rebase the PR as soon as it’s out-of-date. This should preferrably be used by the author, as it requires them to be aware of having to use `git pull --rebase` +### Translations + +Translations are currently handled by Kotti's internal implementation. However, there is a Crowdin project to make sure that all translations are correct. + +#### Pre-requisites + +- Have crowdin cli installed + - MacOs: + ``` + brew tap crowdin/crowdin + brew install crowdin@3 + ``` + - Linux: + - https://support.crowdin.com/cli-tool/#installation +- Have accesss to https://crowdin.com/project/3yourmind-kotti/en +- Have a crowdin token stored in a local env variable (https://crowdin.com/settings#api-key) + ``` + export CROWDIN_V2_API_KEY=1234567890... + ``` + +#### Push translations + +Whenever new translations are added, edited or removed + +1. Update the Crowdin source file `packages/kotti-ui/source/locales/input.json` with latest translations from the Kotti language file `packages/kotti-ui/source/kotti-i18n/locales/en-US.ts` +2. Push the source file to crowdin + ``` + crowdin push + ``` + +#### Pull translations + +1. Check if translations are ready. It should display `100%` next to the target language. + ``` + crowdin status + ``` +2. Pull latest translations + ``` + crowdin pull + ``` +3. Copy latest translations from the Crowdin language .json file to the corresponding Kotti language file. E.g.: + - From `packages/kotti-ui/source/locales/de.json` + - To `packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts` + ### Linting ```bash diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 0000000000..61b427ddc2 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,8 @@ +api_token: CROWDIN_V2_API_KEY +base_path: './packages/kotti-ui' +files: + - dest: '/source/locales/input.json' + source: '/source/locales/input.json' + translation: '/source/locales/%two_letters_code%.json' +preserve_hierarchy: true +project_id: '711705' diff --git a/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts b/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts index 0106802e46..b81b6c7767 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/de-DE.ts @@ -19,8 +19,8 @@ module Common { export const deDE: KottiI18n.Messages = { KtBanner: { - expandLabel: 'Öffnen', expandCloseLabel: 'Schließen', + expandLabel: 'Öffnen', }, KtComment: { cancelMessage: 'Drücken Sie die Esc-Taste oder', @@ -63,10 +63,10 @@ export const deDE: KottiI18n.Messages = { HIDDEN: '', INVALID: 'Ungültig', NOT_STARTED: 'Nicht angefangen', + READY_TO_UPLOAD: 'Bereit zum Hochladen bei Einreichung', UPLOADED: 'Hochgeladen', UPLOADED_WITH_ERROR: 'Hochgeladen mit Fehler', UPLOADING: 'Hochladen...', - READY_TO_UPLOAD: 'Bereit zum Hochladen bei Einreichung', }, text: { clickToUpload: 'Zum Hochladen klicken', @@ -88,11 +88,10 @@ export const deDE: KottiI18n.Messages = { }, KtFieldSelects: { loadingText: 'Lädt', - noMatchText: 'Nichts gefunden', noDataText: 'Keine Ergebnisse', + noMatchText: 'Nichts gefunden', placeholder: 'Daten wählen', }, - // TODO check KtFilters translations KtFilters: { addFilterLabel: 'Filter Hinzufügen', andLabel: 'Und', diff --git a/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts b/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts index 9941cf2ddf..ff4a75aece 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/en-US.ts @@ -19,8 +19,8 @@ module Common { export const enUS: KottiI18n.Messages = { KtBanner: { - expandLabel: 'View', expandCloseLabel: 'Close', + expandLabel: 'View', }, KtComment: { cancelMessage: 'Press Esc key or', @@ -63,10 +63,10 @@ export const enUS: KottiI18n.Messages = { HIDDEN: '', INVALID: 'Invalid', NOT_STARTED: 'Not started', + READY_TO_UPLOAD: 'Ready to upload on submittal', UPLOADED: 'Uploaded', UPLOADED_WITH_ERROR: 'Uploaded with error', UPLOADING: 'Uploading...', - READY_TO_UPLOAD: 'Ready to upload on submittal', }, text: { clickToUpload: 'Click to upload', @@ -88,8 +88,8 @@ export const enUS: KottiI18n.Messages = { }, KtFieldSelects: { loadingText: 'Loading', - noMatchText: 'No matching data', noDataText: 'No results', + noMatchText: 'No matching data', placeholder: 'Select', }, KtFilters: { diff --git a/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts b/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts index 853e19d120..753b3e78d7 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/es-ES.ts @@ -11,7 +11,7 @@ module Common { GREATER_THAN_OR_EQUAL: 'es mayor o igual a', IS_EMPTY: Common.isEmpty, LESS_THAN: 'es menor que', - LESS_THAN_OR_EQUAL: 'es menor que o igual a', + LESS_THAN_OR_EQUAL: 'es menor o igual a', } export const restrictedAccess = 'Acceso restringido' @@ -19,11 +19,11 @@ module Common { export const esES: KottiI18n.Messages = { KtBanner: { - expandLabel: 'Ver', expandCloseLabel: 'Cerrar', + expandLabel: 'Ver', }, KtComment: { - cancelMessage: 'Presione la tecla Esc o', + cancelMessage: 'Pulse la tecla Esc o', clickToCancelLabel: 'haga clic para cancelar', deleteButton: 'Borrar', editButton: 'Editar', @@ -40,15 +40,15 @@ export const esES: KottiI18n.Messages = { acceptPhoto: 'Usar Foto', cancel: 'Cancelar', nextCamera: 'Siguiente Cámara', - rejectPhoto: 'Retomar Foto', + rejectPhoto: 'Volver a tomar Foto', retry: 'Reintentar', takePhoto: 'Tomar Foto', }, error: { - multipleNotAllowed: 'Carga de múltiples archivos no permitida', + multipleNotAllowed: 'No se permite cargar múltiples archivos', notAllowed: 'Permiso denegado para usar la cámara', - notFound: 'No se encontró ninguna cámara disponible', - notSupported: 'No se encontró ninguna cámara compatible', + notFound: 'No se ha encontrado ninguna cámara disponible', + notSupported: 'No se ha encontrado ninguna cámara compatible', }, label: { capture: 'Tomar Foto', @@ -59,24 +59,24 @@ export const esES: KottiI18n.Messages = { }, statusMsg: { CANCELED: 'Cancelado', - ERROR: 'La carga falló, por favor intente nuevamente', + ERROR: 'Carga fallida, por favor inténtelo de nuevo', HIDDEN: '', INVALID: 'Inválido', NOT_STARTED: 'No iniciado', + READY_TO_UPLOAD: 'Listo para cargar en el envío', UPLOADED: 'Cargado', UPLOADED_WITH_ERROR: 'Cargado con error', UPLOADING: 'Cargando...', - READY_TO_UPLOAD: 'Listo para cargar en el envío', }, text: { clickToUpload: 'Haga clic para cargar', dragAndDrop: 'o arrastre y suelte', - learnMore: 'Aprender más', + learnMore: 'Más información', max: 'máx.', }, validationMsg: { - INVALID_EXTENSION: 'Formato de archivo no soportado', - MAX_SIZE_EXCEEDED: 'Tamaño máximo de archivo permitido excedido', + INVALID_EXTENSION: 'Formato de archivo no compatible', + MAX_SIZE_EXCEEDED: 'Se ha excedido el tamaño máximo de archivo permitido', }, }, KtFieldInlineEdit: { @@ -84,28 +84,28 @@ export const esES: KottiI18n.Messages = { }, KtFields: { optionalLabel: 'Opcional', - requiredMessage: 'Este campo es requerido', + requiredMessage: 'Este campo es obligatorio', }, KtFieldSelects: { loadingText: 'Cargando', - noMatchText: 'No hay datos que coincidan', noDataText: 'Sin resultados', + noMatchText: 'No hay datos que coincidan', placeholder: 'Seleccionar', }, KtFilters: { - addFilterLabel: 'Agregar Filtro', + addFilterLabel: 'Añadir Filtro', andLabel: 'Y', boolean: { - EQUAL: 'está', + EQUAL: Common.is, IS_EMPTY: Common.isEmpty, }, - clearAllLabel: 'Limpiar Todo', + clearAllLabel: 'Borrar Todo', currency: Common.number, dateRange: { IN_RANGE: 'está en el rango', IS_EMPTY: Common.isEmpty, }, - emptyListLabel: 'No hay Filtros Aplicados', + emptyListLabel: 'No se han aplicado filtros', filterLabel: 'Filtro', filtersLabel: 'Filtros', float: Common.number, @@ -124,20 +124,20 @@ export const esES: KottiI18n.Messages = { EQUAL: Common.is, IS_EMPTY: Common.isEmpty, }, - unsetLabel: 'No Establecido', - whereLabel: 'Donde', + unsetLabel: 'No establecido', + whereLabel: 'Dónde', }, KtFormSubmit: { errorsSectionTitle: 'Errores', title: 'Envío de formulario no permitido', - warningsSectionTitle: 'Avisos', + warningsSectionTitle: 'Advertencias', }, KtNavbar: { - menuCollapse: 'Colapsar menú', + menuCollapse: 'Contraer el menú', menuExpand: 'Expandir el menú', quickLinksTitle: 'Enlaces rápidos', }, KtValueLabel: { - notSet: 'No establecido', + notSet: 'No Establecido', }, } diff --git a/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts b/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts index 23ba0fccd6..63ce56d7d6 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/fr-FR.ts @@ -19,8 +19,8 @@ module Common { export const frFR: KottiI18n.Messages = { KtBanner: { - expandLabel: 'Voir', expandCloseLabel: 'Fermer', + expandLabel: 'Voir', }, KtComment: { cancelMessage: 'Appuyez sur la touche Esc ou', @@ -63,10 +63,10 @@ export const frFR: KottiI18n.Messages = { HIDDEN: '', INVALID: 'Invalide', NOT_STARTED: 'Pas commencé', + READY_TO_UPLOAD: 'Prêt à être téléchargé', UPLOADED: 'Téléchargé', UPLOADED_WITH_ERROR: 'Téléchargé avec erreur', UPLOADING: 'Téléchargement...', - READY_TO_UPLOAD: 'Prêt à être téléchargé', }, text: { clickToUpload: 'Cliquer pour télécharger', @@ -88,8 +88,8 @@ export const frFR: KottiI18n.Messages = { }, KtFieldSelects: { loadingText: 'Chargement', - noMatchText: 'Aucune correspondance', noDataText: 'Aucun résultat', + noMatchText: 'Aucune correspondance', placeholder: 'Choisir', }, KtFilters: { diff --git a/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts b/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts index 95bfa5ca4c..8233d708a9 100644 --- a/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts +++ b/packages/kotti-ui/source/kotti-i18n/locales/ja-JP.ts @@ -19,8 +19,8 @@ module Common { export const jaJP: KottiI18n.Messages = { KtBanner: { - expandLabel: '表示', expandCloseLabel: '閉じる', + expandLabel: '表示', }, KtComment: { cancelMessage: 'Escキーを押すか', @@ -63,10 +63,10 @@ export const jaJP: KottiI18n.Messages = { HIDDEN: '', INVALID: '無効', NOT_STARTED: '始まっていない', + READY_TO_UPLOAD: '提出時にアップロードする準備ができました', UPLOADED: 'アップロード済み', UPLOADED_WITH_ERROR: 'エラーでアップロードされました', UPLOADING: 'アップロード中...', - READY_TO_UPLOAD: '提出時にアップロードする準備ができました', }, text: { clickToUpload: 'クリックしてアップロード', @@ -88,11 +88,10 @@ export const jaJP: KottiI18n.Messages = { }, KtFieldSelects: { loadingText: 'ロード中', - noMatchText: 'データなし', noDataText: '結果がありません', + noMatchText: 'データなし', placeholder: '選択してください', }, - // TODO check KtFilters translations KtFilters: { addFilterLabel: 'フィルタを追加', andLabel: 'そして', diff --git a/packages/kotti-ui/source/locales/input.json b/packages/kotti-ui/source/locales/input.json new file mode 100644 index 0000000000..07acefe9b4 --- /dev/null +++ b/packages/kotti-ui/source/locales/input.json @@ -0,0 +1,115 @@ +{ + "common": { + "equal": "is equal to", + "greaterThan": "is greater than", + "greaterThanOrEqual": "is greater than or equal to", + "is": "is", + "isEmpty": "is empty", + "lessThan": "is less than", + "lessThanOrEqual": "is less than or equal to", + "restrictedAccess": "Restricted access" + }, + "ktBanner": { + "expandCloseLabel": "Close", + "expandLabel": "View" + }, + "ktComment": { + "cancelMessage": "Press Esc key or", + "clickToCancelLabel": "click to cancel", + "deleteButton": "Delete", + "editButton": "Edit", + "editedLabel": "Edited", + "postButton": "Post", + "replyButton": "Reply", + "replyToLabel": "Reply to", + "unlockedHelpText": "Visible to all users" + }, + "ktFieldFileUpload": { + "button": { + "acceptPhoto": "Use Photo", + "cancel": "Cancel", + "nextCamera": "Next Camera", + "rejectPhoto": "Retake Photo", + "retry": "Retry", + "takePhoto": "Take Photo" + }, + "error": { + "multipleNotAllowed": "Multiple files upload not allowed", + "notAllowed": "Permission denied to use the camera", + "notFound": "No available camera found", + "notSupported": "No compatible camera found" + }, + "label": { + "capture": "Take Photo", + "error": "Error", + "review": "Review", + "unknown": "Unknown" + }, + "statusMsg": { + "canceled": "Canceled", + "error": "Upload failed, please try again", + "invalid": "Invalid", + "notStarted": "Not started", + "readyToUpload": "Ready to upload on submittal", + "uploaded": "Uploaded", + "uploadedWithError": "Uploaded with error", + "uploading": "Uploading..." + }, + "text": { + "clickToUpload": "Click to upload", + "dragAndDrop": "or drag and drop", + "learnMore": "Learn more", + "max": "max." + }, + "validationMsg": { + "invalidExtension": "File format not supported", + "maxSizeExceeded": "Maximum file size allowed exceeded" + } + }, + "ktFieldInlineEdit": { + "placeholder": "Click to Edit" + }, + "ktFields": { + "optionalLabel": "Optional", + "requiredMessage": "This field is required" + }, + "ktFieldSelects": { + "loadingText": "Loading", + "noDataText": "No results", + "noMatchText": "No matching data", + "placeholder": "Select" + }, + "ktFilters": { + "addFilterLabel": "Add Filter", + "andLabel": "And", + "clearAllLabel": "Clear All", + "dateRange": { + "inRange": "is in range" + }, + "emptyListLabel": "No Filters Applied", + "filterLabel": "Filter", + "filtersLabel": "Filters", + "multiEnum": { + "oneOf": "is one of" + }, + "searchLabel": "Search", + "string": { + "contains": "contains" + }, + "unsetLabel": "Unset", + "whereLabel": "Where" + }, + "ktFormSubmit": { + "errorsSectionTitle": "Errors", + "title": "Form Submission Not Allowed", + "warningsSectionTitle": "Warnings" + }, + "ktNavbar": { + "menuCollapse": "Collapse menu", + "menuExpand": "Expand menu", + "quickLinksTitle": "Quick Links" + }, + "ktValueLabel": { + "notSet": "Not Set" + } +}