diff --git a/README.md b/README.md index 526e5255..63d58420 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,3 @@ -# Development Setup - -### Clone mercurial repository. -```shell -hg clone http://hg.code.sf.net/p/roundup/code roundup -``` - -### Make python virtual env and activate it -```shell -cd roundup -python -m venv . -``` -#### For linux and mac -```shell -source bin/activate -``` -#### For windows powershell -```shell -.\scripts\Activate.ps1 -``` - -### Install roundup -```shell -python -m pip install . -``` - -### Clone tracker only repo. -```shell -git clone https://github.com/UMB-CS-682-Team-03/tracker.git -``` - -### Init the demo tracker with default ./demo -```shell -roundup-demo -b sqlite -``` - -### Move the files from tracker dir to demo dir -#### For linux and mac -```shell -cp -r ./tracker/**/* ./demo -cp -r ./tracker/.git ./tracker/.gitignore ./demo -``` -#### For windows powershell -```shell -xcopy .\tracker .\demo /s #say yes to all -``` - -1. Now you are done setting up developing environment. -2. Open the roundup folder in VScode. -3. Any new changes are only to be done in demo directory. -4. git is initialized to track origin in demo directory (for windows check if .git folder is copied to the demo) - # Roundup - Classhelper ## `` ## Why there is need of `` in the issue tracker? @@ -224,7 +172,81 @@ Copy the following files into your Roundup instance `html` directory: After copying these files, you can use the `` component in your Roundup templates. +# Missing translations +To set up translations for the component, follow these steps: + +1. In your Roundup instance's Demo/locale directory, create a new file if not exists with the name __.po (e.g., de.po for German). + +2. After the header, add the translation entries for the component. +For example next and submit are not being shown for German, you can add: + msgid "submit" + msgstr "gehen" + + msgid "next" + msgstr "nächste" + + msgid "name" + msgstr "name" + +The **msgid** here is case sensitive therefore be careful. + +3. Save the __.po file. + +4. Restart your Roundup instance. +This should get you the missing translations, for more details refer roundup documentation for translations. + +# Development Setup + +### Clone mercurial repository. +```shell +hg clone http://hg.code.sf.net/p/roundup/code roundup +``` + +### Make python virtual env and activate it +```shell +cd roundup +python -m venv . +``` +#### For linux and mac +```shell +source bin/activate +``` +#### For windows powershell +```shell +.\scripts\Activate.ps1 +``` + +### Install roundup +```shell +python -m pip install . +``` + +### Clone tracker only repo. +```shell +git clone https://github.com/UMB-CS-682-Team-03/tracker.git +``` + +### Init the demo tracker with default ./demo +```shell +roundup-demo -b sqlite +``` + +### Move the files from tracker dir to demo dir +#### For linux and mac +```shell +cp -r ./tracker/**/* ./demo +cp -r ./tracker/.git ./tracker/.gitignore ./demo +``` +#### For windows powershell +```shell +xcopy .\tracker .\demo /s #say yes to all +``` + +1. Now you are done setting up developing environment. +2. Open the roundup folder in VScode. +3. Any new changes are only to be done in demo directory. +4. git is initialized to track origin in demo directory (for windows check if .git folder is copied to the demo) # Running the Test Suite @@ -233,13 +255,12 @@ After copying these files, you can use the `` component in - GeckoDriver (for Firefox) ### Installation Instructions -1. **Selenium Installation**: - Open your terminal or command prompt and run: - python -m pip install selenium==4.18.1 -2. **GeckoDriver Installation**: - pip install geckodriver==0.0.1 +Enable the python virtual environment then proceed to install the following packages +```shell + python -m pip install selenium geckodriver +``` #### To run the code in a Headless Environment: Set "HEADLESS = TRUE " in line 12. @@ -250,4 +271,3 @@ After copying these files, you can use the `` component in ### Run the test suite python test_classhelper.py - diff --git a/html/classhelper.js b/html/classhelper.js index 175d7de9..373e581b 100644 --- a/html/classhelper.js +++ b/html/classhelper.js @@ -31,6 +31,7 @@ const CLASSHELPER_POPUP_URL = "about:blank"; const CLASSHELPER_POPUP_TARGET = "_blank"; const CLASSHELPER_TABLE_SELECTION_NONE = "table-selection-none"; +const CLASSHELPER_TRANSLATION_KEYWORDS = ["apply", "cancel", "next", "prev", "search", "reset"]; const ALTERNATIVE_DROPDOWN_PATHNAMES = { "roles": "/rest/roles" @@ -149,12 +150,6 @@ class ClassHelper extends HTMLElement { const initialRequestURL = ClassHelper.getRestURL(this.trackerBaseURL, this.helpurlProps); - ClassHelper.fetchTranslations() - .catch(error => { - console.warn("Classhelper failed in translating.") - console.error(error); - }); - this.fetchDropdownsData() .catch(error => { // Top level handling for dropdowns errors. @@ -280,20 +275,39 @@ class ClassHelper extends HTMLElement { return; } - let translations = { - "Apply": "Apply", - "Cancel": "Cancel", - "Next": "Next", - "Prev": "Prev", - "Search": "Search", - "Reset": "Reset" - }; - ClassHelper.translations = translations; + const keys = new Set(); + + const classhelpers = document.getElementsByTagName(CLASSHELPER_TAG_NAME); + for (let classhelper of classhelpers) { + if (classhelper.dataset.searchWith) { + classhelper.dataset.searchWith + .split(',') + .forEach(param => { + keys.add(param.split("[]")[0]); + }); + } + + const a = classhelper.querySelector("a"); + if (a && a.dataset.helpurl) { + let searchParams = new URLSearchParams(a.dataset.helpurl.split("?")[1]); + let properties = searchParams.get("properties"); + if (properties) { + properties.split(',').forEach(key => keys.add(key)); + } + } + } + + CLASSHELPER_TRANSLATION_KEYWORDS.forEach(key => keys.add(key)); + + ClassHelper.translations = {}; + for (let key of keys) { + ClassHelper.translations[key] = key; + } let tracker = window.location.pathname.split('/')[1]; let url = new URL(window.location.origin + "/" + tracker); url.searchParams.append("@template", "translation"); - url.searchParams.append("properties", Object.keys(translations).join(',')); + url.searchParams.append("properties", Array.from(keys.values()).join(',')); let resp, json; @@ -321,7 +335,9 @@ class ClassHelper extends HTMLElement { throw new Error(message); } - ClassHelper.translations = json; + for (let entry of Object.entries(json)) { + ClassHelper.translations[entry[0]] = entry[1]; + } } async fetchDropdownsData() { @@ -547,7 +563,7 @@ class ClassHelper extends HTMLElement { const label = document.createElement("label"); label.classList.add("search-label"); // Add class for styling label.setAttribute("for", param); - label.textContent = param + ":"; + label.textContent = ClassHelper.translations[param] + ":"; let input; if (this.dropdownsData[param]) { @@ -602,7 +618,7 @@ class ClassHelper extends HTMLElement { buttonCell.colSpan = 1; const search = document.createElement("button"); - search.textContent = ClassHelper.translations["Search"]; + search.textContent = ClassHelper.translations["search"]; search.classList.add("search-button"); // Add class for styling search.addEventListener("click", (e) => { e.preventDefault(); @@ -615,7 +631,7 @@ class ClassHelper extends HTMLElement { }); const reset = document.createElement("button"); - reset.textContent = ClassHelper.translations["Reset"]; + reset.textContent = ClassHelper.translations["reset"]; reset.classList.add("reset-button"); // Add class for styling reset.addEventListener("click", (e) => { e.preventDefault(); @@ -665,7 +681,7 @@ class ClassHelper extends HTMLElement { const prev = document.createElement("button"); prev.innerHTML = "<"; - prev.setAttribute("aria-label", ClassHelper.translations["Prev"]); + prev.setAttribute("aria-label", ClassHelper.translations["prev"]); prev.setAttribute("disabled", "disabled"); if (prevUrl) { prev.removeAttribute("disabled"); @@ -680,7 +696,7 @@ class ClassHelper extends HTMLElement { const next = document.createElement("button"); next.innerHTML = ">"; - next.setAttribute("aria-label", ClassHelper.translations["Next"]); + next.setAttribute("aria-label", ClassHelper.translations["next"]); next.setAttribute("disabled", "disabled"); if (nextUrl) { next.removeAttribute("disabled"); @@ -714,7 +730,7 @@ class ClassHelper extends HTMLElement { } const cancel = document.createElement("button"); - cancel.textContent = ClassHelper.translations["Cancel"]; + cancel.textContent = ClassHelper.translations["cancel"]; cancel.addEventListener("click", () => { this.dispatchEvent(new CustomEvent("valueSelected", { detail: { @@ -726,7 +742,7 @@ class ClassHelper extends HTMLElement { const apply = document.createElement("button"); apply.id = "popup-apply"; apply.classList.add("popup-apply"); - apply.textContent = ClassHelper.translations["Apply"]; + apply.textContent = ClassHelper.translations["apply"]; apply.addEventListener("click", () => { this.dispatchEvent(new CustomEvent("valueSelected", { detail: { @@ -774,7 +790,7 @@ class ClassHelper extends HTMLElement { headers.forEach(header => { const th = document.createElement('th'); - th.textContent = header; + th.textContent = ClassHelper.translations[header]; headerRow.appendChild(th); }); thead.appendChild(headerRow); @@ -830,19 +846,7 @@ class ClassHelper extends HTMLElement { } // Create table footer with the same column values as headers - const footerRow = document.createElement('tr'); - - if (includeCheckbox) { - let footThx = document.createElement("th"); - footThx.textContent = "X"; - footerRow.appendChild(footThx); - } - - headers.forEach(header => { - const th = document.createElement('th'); - th.textContent = header; - footerRow.appendChild(th); - }); + const footerRow = headerRow.cloneNode(true); tfoot.appendChild(footerRow); // Assemble the table @@ -1268,7 +1272,7 @@ function enableClassHelper() { return; } - /**@todo - make api call? get 404 then early return? */ + /** make api call if error then do not register*/ // http://localhost/demo/rest fetch("rest") @@ -1279,6 +1283,11 @@ function enableClassHelper() { return; } customElements.define(CLASSHELPER_TAG_NAME, ClassHelper); + ClassHelper.fetchTranslations() + .catch(error => { + console.warn("Classhelper failed in translating.") + console.error(error); + }); }).catch(err => { console.error(err); }); diff --git a/locale/de.po b/locale/de.po index 95270b9e..50223ac8 100644 --- a/locale/de.po +++ b/locale/de.po @@ -13,23 +13,47 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Poedit-Bookmarks: 75,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" -msgid "Submit" -msgstr "gehen" - -msgid "Next" +msgid "next" msgstr "nächste" -msgid "Prev" +msgid "prev" msgstr "vorherige" -msgid "Apply" +msgid "apply" msgstr "anwenden" -msgid "Cancel" +msgid "cancel" msgstr "stornieren" -msgid "Search" +msgid "search" msgstr "suchen" -msgid "Reset" -msgstr "zurücksetzen" \ No newline at end of file +msgid "reset" +msgstr "zurücksetzen" + +msgid "id" +msgstr "id" + +msgid "title" +msgstr "titel" + +msgid "status" +msgstr "status" + +msgid "keyword" +msgstr "schlagwort" + +msgid "username" +msgstr "nutzername" + +msgid "roles" +msgstr "rollen" + +msgid "realname" +msgstr "echtername" + +msgid "address" +msgstr "adresse" + +msgid "name" +msgstr "name"