Leaderboard
Popular Content
Showing content with the highest reputation since 01/07/26 in all areas
-
It's been a dream for many people for years.4 points
-
Some time ago I wrestled working with Safari iPad issues and came up with this on screen console work around. Happy to share. Project1.zip2 points
-
I find this method very practical, so I wrote the following code and wanted to share it with you. If you have any suggestions for improvement, please let me know. function afterrender(sender, eOpts) { var combo = sender; Ext.onReady(function() { var findPanel = function() { var panel = null; if (window.uniGUI && window.uniGUI.Forms && window.uniGUI.Forms.MainForm) { panel = window.uniGUI.Forms.MainForm.UniPanel1; } if (!panel && window.MainForm) { panel = window.MainForm.UniPanel1; } if (!panel) { panel = Ext.ComponentManager.get('UniPanel1'); } return panel; }; var panel = findPanel(); if (panel) { console.log('Panel found:', panel); panel.getEl().setStyle({ 'position': 'absolute', 'z-index': '10000', 'display': 'none', 'overflow': 'auto' }); var isManualCollapse = false; combo.on('expand', function() { isManualCollapse = false; var picker = combo.getPicker(); if (picker) { picker.el.hide(); } panel.show(); var comboEl = combo.getEl(); var comboXY = comboEl.getXY(); var comboBox = comboEl.getBox(); var rect = { x: comboXY[0], y: comboXY[1], width: comboBox.width, height: comboBox.height }; var viewport = Ext.getBody().getViewSize(); var panelWidth = rect.width; var panelOriginalWidth = panel.getWidth(); if (panelOriginalWidth && panelOriginalWidth > rect.width) { panelWidth = panelOriginalWidth; } if (rect.x + panelWidth > viewport.width - 10) { panelWidth = Math.max(rect.width, viewport.width - rect.x - 20); } var panelHeight = panel.getHeight() || 250; var maxHeight = viewport.height - rect.y - rect.height - 20; if (panelHeight > maxHeight) { panelHeight = maxHeight; } var panelY = rect.y + rect.height; if (panelY + panelHeight > viewport.height - 10) { panelY = Math.max(10, rect.y - panelHeight); } var panelX = rect.x; if (panelX + panelWidth > viewport.width - 10) { panelX = viewport.width - panelWidth - 10; } console.log('Positioning:', { combo: {x: rect.x, y: rect.y, width: rect.width, height: rect.height}, panel: {x: panelX, y: panelY, width: panelWidth, height: panelHeight}, viewport: viewport }); panel.getEl().animate({ to: { x: panelX, y: panelY, width: panelWidth, height: panelHeight }, duration: 100 }); }); combo.on('collapse', function() { if (!isManualCollapse) { if (combo.isExpanded) { combo.expand(); return false; } } else { panel.hide(); } }); panel.on('click', function(e) { e.stopPropagation(); }); panel.on('element', 'click', function(e) { e.stopPropagation(); }); window.closeCustomComboPanel = function() { isManualCollapse = true; combo.collapse(); panel.hide(); }; Ext.getBody().on('click', function(e) { var target = e.target; var comboEl = combo.getEl().dom; var panelEl = panel.getEl().dom; if (!comboEl.contains(target) && !panelEl.contains(target)) { isManualCollapse = true; combo.collapse(); panel.hide(); } }); } else { console.warn('Panel not found, retrying...'); setTimeout(findPanel, 500); } }); }2 points
-
One of the issues I noticed with the TUniCheckComboBox component is that the user has to click outside the component for the list to close after selecting items. I addressed this by adding a blue confirmation button. I also added a Hint section for each item. function afterrender(sender, eOpts) { var minPickerWidth = 450; var picker = sender.getPicker(); if (picker && sender.getWidth() < minPickerWidth) { picker.setWidth(minPickerWidth); picker.minWidth = minPickerWidth; picker.updateLayout(); } var me = sender; if (me._customHeaderAdded) { return; } me._customHeaderAdded = true; var showCount = 1; // 1 or 0 Показать количество выбранных элементов var showTotal = 1; // 1 or 0 Показать общее количество элементов var _insertEditField = function() { var _id = me.getPicker().id; me._customPickerId = _id; var getSelectedCount = function() { var count = 0; var allItems = me.getPicker().el.query('.x-boundlist-item'); for (var i = 0; i < allItems.length; i++) { if (isItemSelected(allItems[i])) { count++; } } return count; }; var getTotalCount = function() { var allItems = me.getPicker().el.query('.x-boundlist-item'); return allItems.length; }; var updateSelectAllButtonText = function() { var selectAllButton = Ext.get(_id + '_selectAllButton'); if (selectAllButton && selectAllButton.dom) { if (showCount === 1) { var selectedCount = getSelectedCount(); if (showTotal === 1) { var totalCount = getTotalCount(); selectAllButton.dom.innerHTML = 'Все(' + selectedCount + '/' + totalCount + ')'; } else { selectAllButton.dom.innerHTML = 'Все(' + selectedCount + ')'; } } else { selectAllButton.dom.innerHTML = 'Все'; } } }; var selectAllFlex = (showCount === 1) ? '2' : '1'; var deselectAllFlex = '1'; var headerContainer = Ext.DomHelper.insertFirst(_id, { tag: 'div', cls: 'custom-header-container', style: 'border-bottom: 1px solid #ddd; background: white;', children: [{ tag: 'div', style: 'padding: 5px;', children: [{ tag: 'div', style: 'position: relative; margin-bottom: 5px; display: flex; gap: 4px;', children: [{ tag: 'div', style: 'position: relative; flex: 1;', children: [{ tag: 'input', type: 'text', id: _id + '_filterEdit', placeholder: 'Поиск...', style: 'width: 100%; padding: 4px; padding-right: 25px; box-sizing: border-box;', title: 'Введите текст для поиска' }, { tag: 'div', html: '×', id: _id + '_clearButton', style: 'position: absolute; right: 5px; top: 50%; transform: translateY(-50%); cursor: pointer; color: #999; font-size: 16px; font-weight: bold; display: none;', title: 'Очистить поиск' }] }, { tag: 'button', type: 'button', id: _id + '_okButton', html: 'Окей', style: 'background: #2196F3; color: white; border: none; border-radius: 3px; padding: 4px 12px; cursor: pointer; font-size: 11px; white-space: nowrap; min-width: 50px; transition: background 0.2s;', title: 'Закрыть список выбора' }] }, { tag: 'div', style: 'display: flex; justify-content: space-between; gap: 4px;', children: [{ tag: 'button', type: 'button', id: _id + '_selectAllButton', html: (showCount === 1) ? ((showTotal === 1) ? 'Все(0/0)' : 'Все(0)') : 'Все', style: 'flex: ' + selectAllFlex + '; min-width: 0; padding: 4px 2px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;', title: 'Выбрать всё' }, { tag: 'button', type: 'button', id: _id + '_deselectAllButton', html: 'Ничего', style: 'flex: ' + deselectAllFlex + '; min-width: 0; padding: 4px 2px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;', title: 'Отменить выбор всех' }] }] }] }); var filterField = Ext.get(_id + '_filterEdit'); var clearButton = Ext.get(_id + '_clearButton'); var selectAllButton = Ext.get(_id + '_selectAllButton'); var deselectAllButton = Ext.get(_id + '_deselectAllButton'); var okButton = Ext.get(_id + '_okButton'); me._customComponents = { filterField: filterField, clearButton: clearButton, selectAllButton: selectAllButton, deselectAllButton: deselectAllButton, okButton: okButton, headerContainer: headerContainer }; var headerHeight = headerContainer.offsetHeight; var adjustPickerHeight = function() { setTimeout(function() { var picker = me.getPicker(); if (picker) { var maxHeight = 300; var listEl = picker.el.down('.x-boundlist-list-ct'); if (listEl) { var currentScrollTop = listEl.getScrollTop(); listEl.setStyle({ 'max-height': (maxHeight - headerHeight) + 'px', 'height': 'auto', 'overflow-y': 'auto' }); var contentHeight = listEl.getHeight(); var calculatedHeight = Math.min(contentHeight + headerHeight, maxHeight); picker.setHeight(calculatedHeight); if (currentScrollTop > 0) { listEl.setScrollTop(currentScrollTop); } } } }, 50); }; var clearFilter = function() { filterField.dom.value = ''; filterField.focus(); clearButton.hide(); var items = me.getPicker().el.query('.x-boundlist-item'); for (var i = 0; i < items.length; i++) { items[i].style.display = ''; } adjustPickerHeight(); }; var isItemSelected = function(item) { var checkbox = item.querySelector('input[type="checkbox"]'); if (checkbox) { return checkbox.checked; } if (item.classList.contains('x-boundlist-selected') || item.classList.contains('x-checkbox-checked') || item.getAttribute('aria-selected') === 'true' || item.getAttribute('aria-checked') === 'true') { return true; } var styles = window.getComputedStyle(item); if (styles.backgroundColor !== 'transparent' && styles.backgroundColor !== 'rgba(0, 0, 0, 0)' && styles.backgroundColor !== '') { return true; } return false; }; var bulkSelection = function(select) { var visibleItems = me.getPicker().el.query('.x-boundlist-item:not([style*="display: none"])'); var store = me.getStore(); var values = me.getValue() || []; var newValues = select ? [] : values.slice(); if (select) { for (var i = 0; i < visibleItems.length; i++) { var item = visibleItems[i]; var recordIndex = item.getAttribute('data-recordIndex') || Array.prototype.indexOf.call(item.parentNode.children, item); var record = store.getAt(recordIndex); if (record) { var value = record.get(me.valueField || me.displayField); if (newValues.indexOf(value) === -1) { newValues.push(value); } } } } else { for (var i = 0; i < visibleItems.length; i++) { var item = visibleItems[i]; var recordIndex = item.getAttribute('data-recordIndex') || Array.prototype.indexOf.call(item.parentNode.children, item); var record = store.getAt(recordIndex); if (record) { var value = record.get(me.valueField || me.displayField); var valueIndex = newValues.indexOf(value); if (valueIndex !== -1) { newValues.splice(valueIndex, 1); } } } } me.setValue(newValues); setTimeout(function() { updateSelectAllButtonText(); if (filterField && filterField.dom) { filterField.focus(); } }, 50); }; var selectAllVisible = function() { bulkSelection(true); }; var deselectAllVisible = function() { bulkSelection(false); }; var handleClearButtonClick = function() { setTimeout(function() { updateSelectAllButtonText(); }, 10); }; var findAndBindClearButton = function() { var pickerEl = me.getPicker().el; var clearButtons = pickerEl.query('.x-form-clear-trigger'); if (clearButtons.length > 0) { for (var i = 0; i < clearButtons.length; i++) { var clearBtn = clearButtons[i]; if (!clearBtn._customClearHandlerAdded) { Ext.get(clearBtn).on('click', handleClearButtonClick); clearBtn._customClearHandlerAdded = true; } } } }; clearButton.on('click', clearFilter); selectAllButton.on('click', selectAllVisible); deselectAllButton.on('click', deselectAllVisible); okButton.on('click', function() { me.collapse(); }); // Add hover effect for the OK button okButton.on('mouseover', function() { okButton.setStyle('background', '#1976D2'); okButton.dom.style.cursor = 'pointer'; }); okButton.on('mouseout', function() { okButton.setStyle('background', '#2196F3'); }); me.on('change', function() { setTimeout(function() { updateSelectAllButtonText(); }, 10); }); filterField.on('keyup', function() { var searchText = filterField.dom.value.toLowerCase(); if (searchText.length > 0) { clearButton.show(); } else { clearButton.hide(); } var items = me.getPicker().el.query('.x-boundlist-item'); var visibleCount = 0; for (var i = 0; i < items.length; i++) { var text = items[i].textContent || items[i].innerText; if (text.toLowerCase().indexOf(searchText) !== -1) { items[i].style.display = ''; visibleCount++; } else { items[i].style.display = 'none'; } } adjustPickerHeight(); }); me.getPicker().el.on('click', function(event) { var target = event.target; var item = target.closest('.x-boundlist-item'); if (item) { setTimeout(function() { updateSelectAllButtonText(); }, 10); } if (target.classList.contains('x-form-clear-trigger')) { handleClearButtonClick(); } }); me.on('expand', function() { setTimeout(function() { updateSelectAllButtonText(); findAndBindClearButton(); if (filterField && filterField.dom) { filterField.focus(); } adjustPickerHeight(); }, 100); }); me.on('collapse', function() { filterField.dom.value = ''; clearButton.hide(); var items = me.getPicker().el.query('.x-boundlist-item'); for (var i = 0; i < items.length; i++) { items[i].style.display = ''; } }); setTimeout(function() { findAndBindClearButton(); }, 200); adjustPickerHeight(); me.on('expand', adjustPickerHeight); me.getPicker().un('show', _insertEditField); }; me.getPicker().on('show', _insertEditField); me.on('destroy', function() { if (me._customComponents) { try { if (me._customComponents.headerContainer) { if (me._customComponents.headerContainer.dom) { me._customComponents.headerContainer.remove(); } else if (me._customComponents.headerContainer.parentNode) { me._customComponents.headerContainer.parentNode.removeChild(me._customComponents.headerContainer); } } } catch (e) { console.log('Error during cleanup:', e); } me._customComponents = null; } me._customHeaderAdded = false; }); }2 points
-
Please apply the settings you see in the image in your desktop program, and then open the site on your mobile. These settings will help the program designed for desktop to be displayed reasonably well on mobile as well.2 points
-
Hello everyone, using the demo of this product I think it's great, I don't know why the community doesn't talk about it much, or why the people from UniGUI don't talk about it. In my case it's important because the developments I do have to be made for both desktop and mobile, I have two logins, two menus, etc.2 points
-
1 point
-
Unfortunately your question is very vague. Can you be more specific or give an example of what you are trying to do.1 point
-
In fact, the reason you want the “real client path” (like C:\Users\...\Downloads\...) is because you want to use it in a PowerShell script to copy the file to another server. However, a web application (including UniGUI) cannot access the user’s real local file path or run PowerShell on the client PC. This is restricted by browser security. The browser only allows you to select a file and upload it, but it does not expose the original folder path. That’s why UniGUI gives you only the server-side temporary path (cache folder) after upload, and this is expected behavior. The only alternatives are: Upload the file to the server (UniGUI cache or your custom folder) and then copy/transfer it from the server to another server (PowerShell can be executed on the server side). Use a separate installed client-side solution, for example: a desktop application/agent, a Windows service, or a browser extension, which can access the local file system and execute PowerShell locally. So the correct workflow for UniGUI is: Client selects file → Upload to server → Work with the server file path → Copy/Transfer1 point
-
1 point
-
Hello, procedure TMainForm.UniFormCreate(Sender: TObject); begin UniSyntaxEditEx1.BorderStyle := ubsNone; end;1 point
-
1 point
-
Hello ! Thanks so much. It works. I updated last week to the newest version. I did not find this settings in the older version. Regards1 point
-
1 point
-
1 point
-
1 point
-
Проблему в рабочем приложении решить удалось. Использовал Вашу 1-ую рекомендацию. Связывание (через add) наследника от Ext.draw.Container c TuniPanel сделал не сразу после создания (как было до этого), а уже после непосредственного show() и всё стало нормально работать без каких-либо ошибок. Спасибо!1 point
-
1 point
-
1 point
-
Спасибо, Ваши комментарии очень интересны, подумаю над ними.1 point
-
Вероятнее всего, проблема связана с тем, что Ext.draw.Container получает события resize/layout до того, как его внутренние canvas-элементы полностью отрендерены и добавлены в DOM. В качестве решения можно попробовать: инициализировать и добавлять draw-контейнер только после afterrender или непосредственно после show(), а не в скрытом состоянии; откладывать операции resize/перерисовки через Ext.defer(...); перед обращением к canvas.dom проверять, что элемент уже существует; принудительно вызывать перерасчёт размеров графика после первого отображения компонента. В тестовом проекте ошибка, скорее всего, не воспроизводится из-за более простой структуры и меньшего количества асинхронных layout/resize операций. В рабочем проекте при большем количестве компонентов и переключений возникает race condition между render и resize, которая и приводит к описанной ошибке.1 point
-
Добрый, Не пробовали это событие?: https://www.unigui.com/doc/online_help/api/uniHTMLFrame_TUniHTMLFrame_OnUpdateHtml.html1 point
-
Hello, The Accept-Encoding header is sent by the web client/browser to indicate supported encodings, and the server uses this information for content negotiation. It is controlled by the client, not by the server.1 point
-
не бачу ajaxRequest в js. Саме він робить події які потім приходять до OnAjaxEvent1 point
-
So I am looking at the demo, and it works nicely, like I would like my application to do @Stemon63. Is there, like, any checklist you can make in order for me to try and replicate this in my environment? Things to be added etc.1 point
-
Hi everyone, IAMClient4D 2.0.0 🎉 I’ve just published v2.0.0 of IAMClient4D, a Delphi library designed to integrate Keycloak / OAuth2 / OpenID Connect in a solid, production-ready way. What’s new in this major release: Synchronous API + async wrapper (cleaner server-side code, simpler UI integration) Stronger JWT validation (RS256/HS*, claim validation, JWKS caching, clock skew handling) Security hardening (important fixes in validation/claims and crypto utilities) Encrypted storage and configurable crypto providers Updated documentation: improved README, updated examples, and an official CHANGELOG. Full changelog here: https://github.com/claudio-piffer/IAMClient4D/blob/master/CHANGELOG.md1 point
-
🧩 Grid State Saver Script (UniGUI / ExtJS) 👉 Just paste this script into the ClientEvents → ExtEvents → afterrender event of your TUniDBGrid, and it will automatically handle everything for you. This lightweight script is responsible for saving and restoring the grid layout (state) in a simple and reliable way — including: ✅ Column movement (columnmove) ✅ Column resizing (columnresize) ✅ Column visibility (columnshow / columnhide) ✅ Sorting (sortchange) All layout changes (visibility, order, and width) are saved in the browser’s localStorage, ensuring the user’s grid layout persists between sessions without any backend or database setup. Extra features: 🔹 Adds a “Restore Default Columns” button in the grid’s header menu, with a confirmation dialog. 🔹 Built-in i18n (translation support) — all text strings are centralized at the top of the script for easy language customization. 💡 Perfect for providing a consistent and user-friendly experience, keeping each user’s personalized grid layout exactly as they left it. GridSaveState.zip function afterrender(sender, eOpts) { // ==== TRADUÇÃO / I18N ==== // basta ajustar essas variáveis conforme o idioma desejado var i18n = { btnRestore: 'Restaurar colunas padrão', confirmTitle: 'Confirmar', confirmMsg: 'Deseja realmente restaurar as configurações da grade?', toastMsg: 'Configurações restauradas com sucesso.', iconUndo: 'x-fa fa-undo' }; var STATE_KEY = 'MinhaTela'; //ID único por UniDBGrid var _restoring = false; // ==== localStorage ==== function saveLS(key, obj) { try { localStorage.setItem(key, JSON.stringify(obj)); } catch (e) { } } function loadLS(key) { try { var raw = localStorage.getItem(key); return raw ? JSON.parse(raw) : null; } catch (e) { return null; } } function removeLS(key) { try { localStorage.removeItem(key); } catch (e) { } } // ==== helpers ==== function normText(t) { return (t ? String(t).replace(/<[^>]+>/g, '').trim().toLowerCase() : ''); } function keyOf(c) { var di = (c.getDataIndex ? c.getDataIndex() : c.dataIndex); if (di !== undefined && di !== null && di !== '') return 'di:' + di; if (c.initialConfig && c.initialConfig.dataIndex) return 'di:' + c.initialConfig.dataIndex; if (c.text) return 'tx:' + normText(c.text); return 'id:' + (c.reference || c.itemId || c.id); } function getHeader(grid) { return grid.getHeaderContainer ? grid.getHeaderContainer() : (grid.headerCt || null); } function getLeafColumns(grid) { var h = getHeader(grid); if (!h) return []; return h.getGridColumns ? h.getGridColumns() : (grid.getColumns ? grid.getColumns() : []); } // ==== capturar ==== function captureState(grid) { var cols = getLeafColumns(grid); if (!cols || !cols.length) return null; var out = []; for (var i = 0; i < cols.length; i++) { var c = cols[i], k = keyOf(c); if (!k) continue; out.push({ key: k, hidden: (typeof c.isHidden === 'function') ? c.isHidden() : !!c.hidden, width: (typeof c.getWidth === 'function') ? c.getWidth() : c.width, index: i }); } var sorters = []; var store = (grid.getStore && grid.getStore()) || grid.store; if (store && store.getSorters && store.getSorters()) { store.getSorters().each(function (s) { if (s.getProperty && s.getDirection) sorters.push({ property: s.getProperty(), direction: s.getDirection() }); }); } return { columns: out, sorters: sorters }; } // ==== aplicar ==== function applyState(grid, state) { if (!state || !state.columns || !state.columns.length) return; var header = getHeader(grid); if (!header) return; var cols = getLeafColumns(grid); var colByKey = {}; for (var i = 0; i < cols.length; i++) colByKey[keyOf(cols[i])] = cols[i]; _restoring = true; Ext.suspendLayouts(); for (var j = 0; j < state.columns.length; j++) { var s = state.columns[j], col = colByKey[s.key]; if (!col) continue; if (typeof col.setHidden === 'function') col.setHidden(!!s.hidden); else col.hidden = !!s.hidden; if (s.width && s.width > 0 && typeof col.setWidth === 'function') col.setWidth(s.width); } var ordered = state.columns.slice(0).sort(function (a, b) { return a.index - b.index; }); for (var k = 0; k < ordered.length; k++) { var sk = ordered[k], colK = colByKey[sk.key]; if (!colK) continue; var curIdx = header.items.indexOf(colK); if (curIdx !== -1 && curIdx !== k) header.move(curIdx, k); } var store = (grid.getStore && grid.getStore()) || grid.store; if (store && state.sorters && state.sorters.length && store.sort) store.sort(state.sorters); Ext.resumeLayouts(true); if (header.doLayout) header.doLayout(); if (grid.updateLayout) grid.updateLayout(); Ext.defer(function () { _restoring = false; }, 10); } // ==== persistência ==== function saveStateNow() { if (_restoring) return; var st = captureState(sender); if (st) { saveLS(STATE_KEY, st); ensureResetMenu(sender, true); } } sender.on('columnmove', function () { Ext.defer(saveStateNow, 150); }); sender.on('columnresize', saveStateNow); sender.on('columnshow', saveStateNow); sender.on('columnhide', saveStateNow); sender.on('sortchange', saveStateNow); // ==== restaurar ==== function tryApply() { var st = loadLS(STATE_KEY); if (!st) return true; var h = getHeader(sender); if (!h || !h.rendered) return false; var cols = getLeafColumns(sender); if (!cols || !cols.length) return false; applyState(sender, st); ensureResetMenu(sender, true); return true; } var attempts = 0, timer = Ext.interval(function () { attempts++; if (tryApply() || attempts > 12) Ext.uninterval(timer); }, 160); sender.on('viewready', function () { tryApply(); }, null, { single: true }); sender.on('reconfigure', function () { tryApply(); }); // ==== adicionar botão "Restaurar padrão" com confirmação ==== function ensureResetMenu(grid, show) { try { var header = getHeader(grid); if (!header) return; var menu = header.getMenu ? header.getMenu() : null; if (!menu) return; var id = 'restore-default-state-item'; var existing = menu.down('#' + id); if (existing) { existing.setVisible(!!show); return; } menu.add('-'); menu.add({ itemId: id, text: i18n.btnRestore, iconCls: i18n.iconUndo, hidden: !show, handler: function () { var doReset = function () { removeLS(STATE_KEY); location.reload(); Ext.toast({ html: i18n.toastMsg, align: 't', slideInDuration: 300, minWidth: 220 }); }; if (Ext && Ext.Msg && Ext.Msg.confirm) { Ext.Msg.confirm(i18n.confirmTitle, i18n.confirmMsg, function (btn) { if (btn === 'yes') doReset(); }); } else { if (window.confirm(i18n.confirmMsg)) doReset(); } } }); } catch (e) { } } sender.on('headercontextmenu', function () { var hasState = !!loadLS(STATE_KEY); ensureResetMenu(sender, hasState); }); sender.clearSavedState = function () { removeLS(STATE_KEY); }; }1 point
-
Hello @bmmsr UniGui fully support Multi Language, I think you didn't implement the RTL correctly in the MainModule Unit. You can Set the language in The LogIn form TUniGUIApplication(UniApplication).Cookies.SetCookie('LANGUAGE', '1', Date+60); // 1 or 0 as you like and After you Change the Language you should restart the Application (UniApplication as TUniGUIApplication).Restart; in the OnCreate of the DataModule var iLang : string; iLang := TUniGUIApplication(UniApplication).Cookies.Values['LANGUAGE']; RTL := (iLang = '1'); now all forms/Frames ParentRTL in the Application must be True; OnCreate of the Form/Frame you can change the Captions in any language you like, but if you are using tsiLang Components it needs specific changes to work correctly in UniGui. Regards1 point
-
on the main page of the company that hired you to create this solution, create 2 buttons one to access the system in Arabic another in English or, create an initial website that will have these 2 buttons, each button accesses the respective website. Each site will open in the predefined language, regardless of the user opening it. It is very likely that your Arabic-speaking user will not access the site in English, and neither will the opposite. so this shouldn't be a user-defined thing. This way, you maintain just one project, but with 2 different installations that can even be on different servers.1 point
-
1 point
