UIUX : Experiment Dolibarr JS context and tools - Add tool for langs (#36389)

* Add more doc and simplify hook systeme uasge

* doc

* doc

* lang tool

* lang tool

* lang tool
This commit is contained in:
John BOTELLA
2025-11-23 19:11:46 +01:00
committed by GitHub
parent 3ab0d148b5
commit 7494d8c2d1
9 changed files with 739 additions and 25 deletions

View File

@@ -253,6 +253,10 @@ class Documentation
'summary' => array(), 'summary' => array(),
), ),
'UxDolibarrContext' => array( 'UxDolibarrContext' => array(
'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/index.php', 1),
'icon' => 'fas fa-flask',
'submenu' => array(
'UxDolibarrContextHowItWork' => array(
'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/index.php', 1), 'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/index.php', 1),
'icon' => 'fas fa-flask', 'icon' => 'fas fa-flask',
'submenu' => array(), 'submenu' => array(),
@@ -267,6 +271,15 @@ class Documentation
'SetAndUseContextVars' => '#titlesection-contextvars', 'SetAndUseContextVars' => '#titlesection-contextvars',
), ),
), ),
'UxDolibarrContextLangsTool' => array(
'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/langs-tool.php', 1),
'icon' => 'fas fa-flask',
'submenu' => array(),
'summary' => array(),
),
),
'summary' => array(),
),
) )
); );
@@ -485,7 +498,13 @@ class Documentation
if ($showsubmenu && !empty($menu['submenu'])) { if ($showsubmenu && !empty($menu['submenu'])) {
foreach ($menu['submenu'] as $key => $item) { foreach ($menu['submenu'] as $key => $item) {
print '<li class="summary-title ">'; print '<li class="summary-title ">';
if (!empty($item['url'])) {
print '<h3 class="level-'.$level.'"><a href="'.dolBuildUrl($item['url']).'" >'.$langs->trans($key).'</a></h3>';
} else {
print '<h3 class="level-'.$level.'">'.$langs->trans($key).'</h3>'; print '<h3 class="level-'.$level.'">'.$langs->trans($key).'</h3>';
}
if ($showsubmenu_summary) { if ($showsubmenu_summary) {
$this->displaySummary($item, $level); $this->displaySummary($item, $level);
} }

View File

@@ -1,10 +1,14 @@
// This file is purely for IDE autocompletion and developer convenience. /** This file is purely for IDE autocompletion and developer convenience.
// It is never executed or loaded in Dolibarr itself. * It is never executed or loaded in Dolibarr itself.
*
// MOCK DEFINITION: Dolibarr.tools * MOCK DEFINITION: Dolibarr.tools
// This mock helps your code editor understand the structure of Dolibarr.tools * This mock helps your code editor understand the structure of Dolibarr.tools
// and provides autocomplete hints, parameter hints, and inline documentation. * and provides autocomplete hints, parameter hints, and inline documentation.
// You can safely edit this file to add all standard Dolibarr tools for autocompletion. * You can safely edit this file to add all standard Dolibarr tools for autocompletion.
*
* @SEE dolibarr-context.umd.js
*
*/
var Dolibarr = { var Dolibarr = {
tools: { tools: {
@@ -22,8 +26,146 @@ var Dolibarr = {
*/ */
setEventMessage: function(msg, type, sticky) {}, setEventMessage: function(msg, type, sticky) {},
/**
* TThe langs tool
*/
langs: {
/**
* Load a single locale from cache or fetch
* @param {string} domain
* @param {string} locale
* @returns {Promise<Object>} translation object
*/
loadLocale(domain, locale) {},
/**
* Load translations for a domain (multiple locales)
* @param {string} domain
* @param {string} locales - comma-separated list
* @returns {Promise<Object>}
*/
load(domain, locales = currentLocale) {},
/**
* Set the current locale to use for translations
* @param {string} locale
*/
setLocale(locale) {},
/**
* Translate a key using current locale
* Supports placeholders like %s, %d, %f (simple sprintf)
* @param {string} key
* @param {...any} args
* @returns {string}
*/
trans(key, ...args) {},
},
// You can add more standard Dolibarr tools here for IDE autocompletion. // You can add more standard Dolibarr tools here for IDE autocompletion.
// Example: // Example:
// alertUser: function(msg) {}, // alertUser: function(msg) {},
} },
/**
* Defines a new secure tool.
* @param {string} name Name of the tool
* @param {*} value Function, class or object
* @param {boolean} overwrite Explicitly allow overwriting an existing tool
*
* See also dolibarr-context.mock.js for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.
*/
defineTool(name, value, overwrite = false, triggerHook = true) {},
/**
* Check if tool exists
* @param {string} name Tool name
* @returns {boolean} true if exists
*/
checkToolExist(name) {},
/**
* Get read-only snapshot of context variables
*/
ContextVars() {},
/**
* Defines a new context variable.
* @param {string} key
* @param {string|number|boolean} value
* @param {boolean} overwrite Allow overwriting existing value
*/
setContextVar(key, value, overwrite = false) {},
/**
* Set multiple context variables
* @param {Object} vars Object of key/value pairs
* @param {boolean} overwrite Allow overwriting existing values
*/
setContextVars(vars, overwrite = false) {},
/**
* Get a context variable safely
* @param {string} key
* @param {*} fallback Optional fallback if variable not set
* @returns {*}
*/
getContextVar(key, fallback = null) {},
/**
* Enable or disable debug mode
* @param {boolean} state
*/
debugMode(state) {},
/**
* Enable or disable debug mode
* @returns {int}
*/
getDebugMode() {},
/**
* Internal logger
* Only prints when debug mode is enabled
* @param {string} msg
*/
log(msg) {},
/**
* Executes a hook-like JS event with CustomEvent.
* @param {string} hookName Hook identifier
* @param {object} data Extra information passed to listeners
*/
executeHook(hookName, data = {}) {},
/**
* Registers an event listener.
* @param {string} eventName Event to listen to
* @param {function} callback Listener function
*/
on(eventName, callback) {},
/**
* Unregister an event listener
* @param {string} eventName
* @param {function} callback
*/
off(eventName, callback) {},
/**
* Register an asynchronous hook
* @param {string} eventName
* @param {function} fn Async function receiving previous result
* @param {Object} opts Optional {before, after, id} to control order
* @returns {string} The hook ID
*/
onAwait(eventName, fn, opts = {}) {},
/**
* Execute async hooks sequentially
* @param {string} eventName
* @param {*} data Input data for first hook
* @returns {Promise<*>} Final result after all hooks
*/
async executeHookAwait(eventName, data) {},
}; };

View File

@@ -393,11 +393,7 @@
console.log("Show this help : %cDolibarr.tools.showConsoleHelp();","font-weight: bold;"); console.log("Show this help : %cDolibarr.tools.showConsoleHelp();","font-weight: bold;");
console.log(`Documentation for admin only on : %cModule builder ➜ UX Components Doc`,"font-weight: bold;"); console.log(`Documentation for admin only on : %cModule builder ➜ UX Components Doc`,"font-weight: bold;");
// -----------------------------------------------------------------------------
// DEBUG MODE // DEBUG MODE
// -----------------------------------------------------------------------------
console.groupCollapsed("Dolibarr debug mode"); console.groupCollapsed("Dolibarr debug mode");
console.log( console.log(
@@ -418,9 +414,7 @@
console.log("Note : debug mode status is persistent."); console.log("Note : debug mode status is persistent.");
console.groupEnd(); console.groupEnd();
// -----------------------------------------------------------------------------
// HOOKS // HOOKS
// -----------------------------------------------------------------------------
console.groupCollapsed("Hooks helpers"); console.groupCollapsed("Hooks helpers");
console.log( console.log(

View File

@@ -0,0 +1,232 @@
document.addEventListener('Dolibarr:Init', function(e) {
/**
* Dolibarr.tools.langs
* --------------------
* Manage translations in JS context with IndexedDB cache, multi-locale support and fallback.
* Parallel loading of language files for performance.
* Automatic cache invalidation if Dolibarr version changes.
*
* Require Dolibarr context vars
* DOL_LANG_INTERFACE_URL, MAIN_LANG_DEFAULT, DOL_VERSION
*
*/
const langs = function() {
const ONE_DAY = 86400000;
let currentLocale = Dolibarr.getContextVar('MAIN_LANG_DEFAULT', 'en_US');
let translations = {}; // { en_US: {KEY: TEXT}, fr_FR: {...} }
let domainsLoaded = {}; // { en_US: Set(['main','other']), fr_FR: Set([...]) }
if (!domainsLoaded[currentLocale]) domainsLoaded[currentLocale] = new Set();
let domainsRequested = new Set(); // Set of domain names that were requested at least once
/**
* Open or create IndexedDB for caching translations
* @returns {Promise<IDBDatabase>}
*/
async function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('DolibarrLangs', 1);
request.onupgradeneeded = e => {
const db = e.target.result;
if (!db.objectStoreNames.contains('langs')) db.createObjectStore('langs');
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* Get cached translation for a domain + locale
* @param {string} domain
* @param {string} locale
* @returns {Promise<Object|null>}
*/
async function getCache(domain, locale) {
try {
const db = await openDB();
const tx = db.transaction('langs', 'readonly');
const store = tx.objectStore('langs');
return new Promise((resolve, reject) => {
const request = store.get(`${domain}@${locale}`);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
} catch (err) {
return null;
}
}
/**
* Set cached translation for a domain + locale
* @param {string} domain
* @param {string} locale
* @param {Object} data
*/
async function setCache(domain, locale, data) {
try {
const db = await openDB();
const tx = db.transaction('langs', 'readwrite');
const store = tx.objectStore('langs');
const dolibarrVersion = Dolibarr.getContextVar('DOL_VERSION', 0);
await store.put({ key: `${domain}@${locale}`, data, timestamp: Date.now(), dolibarrVersion }, `${domain}@${locale}`);
} catch (err) {
// fail silently
Dolibarr.log('Save langs in cache fail');
}
}
/**
* Clear all cached translations in IndexedDB and in-memory
*/
async function clearCache(clearMemory = false) {
if(clearMemory) {
translations = {};
domainsLoaded = {};
}
try {
const db = await openDB();
const tx = db.transaction('langs', 'readwrite');
const store = tx.objectStore('langs');
await store.clear();
Dolibarr.log('Dolibarr.tools.langs: cache cleared');
} catch (err) {
console.error('Dolibarr.tools.langs: failed to clear cache', err);
}
}
/**
* Load a single locale from cache or fetch
* @param {string} domain
* @param {string} locale
* @returns {Promise<Object>} translation object
*/
async function loadLocale(domain, locale) {
const cache = await getCache(domain, locale);
const now = Date.now();
const dolibarrVersion = Dolibarr.getContextVar('DOL_VERSION', 0);
if (cache && cache.data && (now - cache.timestamp < ONE_DAY) && cache.dolibarrVersion === dolibarrVersion) {
Dolibarr.log('Langs tool : Load lang from cache');
return cache.data;
}
const langInterfaceUrl = Dolibarr.getContextVar('DOL_LANG_INTERFACE_URL', false);
if(!langInterfaceUrl) {
console.error('Dolibarr langs: missing DOL_LANG_INTERFACE_URL')
return;
}
Dolibarr.log('Langs tool : Load lang from interface');
const params = new URLSearchParams({ domain, local: locale });
const resp = await fetch(`${langInterfaceUrl}?${params.toString()}`);
const json = await resp.json();
const data = json[locale] || {};
await setCache(domain, locale, data);
return data;
}
/**
* Load translations for a domain (multiple locales)
* @param {string} domain
* @param {string} locales - comma-separated list
* @returns {Promise<Object>}
*/
async function load(domain, locales = currentLocale) {
const list = locales.split(',');
// flag domaine as requested for future load when local change
domainsRequested.add(domain);
const results = await Promise.all(list.map(loc => loadLocale(domain, loc)));
list.forEach((loc, i) => {
if (!translations[loc]) translations[loc] = {};
Object.assign(translations[loc], results[i]);
if (!domainsLoaded[loc]) domainsLoaded[loc] = new Set();
domainsLoaded[loc].add(domain);
});
return translations;
}
/**
* Set the current locale to use for translations
* @param {string} locale
*/
async function setLocale(locale, noDomainReload = false) {
if (!locale || locale === currentLocale) return;
const prev = currentLocale;
currentLocale = locale;
if (!domainsLoaded[locale]) domainsLoaded[locale] = new Set();
if (!noDomainReload) {
// priorité : domainsLoaded[prev], sinon fallback sur domainsRequested
let toReload = Array.from(domainsLoaded[prev] || []);
if (toReload.length === 0) {
// aucun domaine marqué comme "loaded" pour prev : utiliser la liste des domaines demandés
toReload = Array.from(domainsRequested);
}
for (const domain of toReload) {
// load(domain, locale) accepte le param locale ; l'appel charge et met domainsLoaded
if (domainsLoaded[locale].size === 0) {
await load(domain, locale);
}
}
}
Dolibarr.log(`Locale changed: ${prev} -> ${locale}`);
}
/**
* Translate a key using current locale
* Supports placeholders like %s, %d, %f (simple sprintf)
* @param {string} key
* @param {...any} args
* @returns {string}
*/
function trans(key, ...args) {
const text = translations[currentLocale]?.[key] || key;
if (!args.length) return text;
// Utilisation de la fonction sprintf pour le formatage
return sprintf(text, ...args);
}
function sprintf(fmt, ...args) {
let i = 0;
return fmt.replace(/%[%bcdeEfFgGosuxX]/g, (match) => {
if (match === '%%') return '%';
const arg = args[i++];
switch (match) {
case '%s': return String(arg);
case '%d':
case '%u': return Number(arg);
case '%f':
case '%F': return parseFloat(arg);
case '%b': return Number(arg).toString(2);
case '%o': return Number(arg).toString(8);
case '%x': return Number(arg).toString(16);
case '%X': return Number(arg).toString(16).toUpperCase();
case '%c': return String.fromCharCode(Number(arg));
default: return match;
}
});
}
return {
load,
clearCache,
setLocale,
trans,
get currentLocale() { return currentLocale; }
};
};
Dolibarr.defineTool('langs',langs());
});

View File

@@ -68,7 +68,7 @@ $documentation->showSidebar(); ?>
<div class="doc-content-wrapper"> <div class="doc-content-wrapper">
<h1 class="documentation-title"><?php echo $langs->trans($experimentName); ?></h1> <h1 class="documentation-title"><?php echo $langs->trans($experimentName); ?> : <?php echo $langs->trans('UxDolibarrContextHowItWork'); ?></h1>
<?php $documentation->showSummary(); ?> <?php $documentation->showSummary(); ?>

View File

@@ -0,0 +1,110 @@
<?php
/*
* Copyright (C) ---Replace with your own copyright and developer email---
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//if (! defined('NOREQUIREDB')) define('NOREQUIREDB', '1'); // Do not create database handler $db
//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER', '1'); // Do not load object $user
if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC', '1'); // Do not load object $mysoc
//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN', '1'); // Do not load object $langs
//if (! defined('NOSCANGETFORINJECTION')) define('NOSCANGETFORINJECTION', '1'); // Do not check injection attack on GET parameters
//if (! defined('NOSCANPOSTFORINJECTION')) define('NOSCANPOSTFORINJECTION', '1'); // Do not check injection attack on POST parameters
//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Do not roll the Anti CSRF token (used if MAIN_SECURITY_CSRF_WITH_TOKEN is on)
//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK', '1'); // Do not check style html tag into posted data
if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu
//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php
//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1'); // Do not load ajax.lib.php library
//if (! defined("NOLOGIN")) define("NOLOGIN", '1'); // If this page is public (can be called outside logged session). This include the NOIPCHECK too.
//if (! defined('NOIPCHECK')) define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
//if (! defined("MAIN_LANG_DEFAULT")) define('MAIN_LANG_DEFAULT', 'auto'); // Force lang to a particular value
//if (! defined("MAIN_AUTHENTICATION_MODE")) define('MAIN_AUTHENTICATION_MODE', 'aloginmodule'); // Force authentication handler
//if (! defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET
//if (! defined('NOBROWSERNOTIF')) define('NOBROWSERNOTIF', '1'); // Disable browser notification
// Load Dolibarr environment
require '../../../../../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
/**
* INTERFACE FOR JS CONTEXT LANG
*/
if (empty($dolibarr_nocache)) {
$delaycache = '86400';
header('Cache-Control: max-age=' . $delaycache . ', public, must-revalidate');
header('Pragma: cache'); // This is to avoid to have Pragma: no-cache set by proxy or web server
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (int) $delaycache) . ' GMT'); // This is to avoid to have Expires set by proxy or web server
} else {
// If any cache on files were disable by config file (for test purpose)
header('Cache-Control: no-cache');
}
header('Content-Type: application/json; charset=utf-8');
$local = GETPOST('local');
if (empty($local)) {
$local = $langs->getDefaultLang();
}
$domain = GETPOST('domain');
if (empty($domain)) {
echo json_encode(['error' => 'Missing domain']);
exit;
}
if (!preg_match('/^[A-Za-z0-9_-]+(?:@[A-Za-z0-9_-]+)?$/', $domain)) {
echo json_encode(['error' => 'Invalid domain']);
exit;
}
if (!preg_match('/^[a-z]{1,2}_[A-Z]{1,2}(?:,[a-z]{1,2}_[A-Z]{1,2})*$/', $local)) {
echo json_encode(['error' => 'Invalid langs codes']);
exit;
}
/*
Format for JS:
{
fr_FR : {KEY:"TEXT", ...},
en_US : {KEY:"TEXT", ...}
}
*/
$locals = explode(',', $local);
$json = new stdClass();
foreach ($locals as $langCode) {
$json->$langCode = [];
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($langCode);
$outputlangs->load($domain);
foreach ($outputlangs->tab_translate as $k => $v) {
$json->$langCode[$k] = dolPrintHTML($v); // to escape js and other stuff
}
}
print json_encode($json, JSON_PRETTY_PRINT);

View File

@@ -0,0 +1,207 @@
<?php
/*
* Copyright (C) 2025 Anthony Damhet <a.damhet@progiseize.fr>
* Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Load Dolibarr environment
require '../../../../../../main.inc.php';
/**
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Protection if external user
if ($user->socid > 0) {
accessforbidden();
}
// Includes
require_once DOL_DOCUMENT_ROOT . '/admin/tools/ui/class/documentation.class.php';
// Load documentation translations
$langs->load('uxdocumentation');
//
$documentation = new Documentation($db);
$group = 'ExperimentalUx';
$experimentName = 'UxDolibarrContextLangsTool';
$experimentAssetsPath = $documentation->baseUrl . '/experimental/experiments/dolibarr-context/assets/';
$js = [
'/includes/ace/src/ace.js',
'/includes/ace/src/ext-statusbar.js',
'/includes/ace/src/ext-language_tools.js',
$experimentAssetsPath . '/dolibarr-context.umd.js',
$experimentAssetsPath . '/dolibarr-tool.langs.js',
$experimentAssetsPath . '/dolibarr-tool.seteventmessage.js',
];
$css = [];
// Output html head + body - Param is Title
$documentation->docHeader($langs->trans($experimentName, $group), $js, $css);
// Set view for menu and breadcrumb
$documentation->view = [$group, 'UxDolibarrContext', $experimentName];
// Output sidebar
$documentation->showSidebar(); ?>
<script>
Dolibarr.setContextVars(<?php print json_encode([
'DOL_VERSION' => DOL_VERSION,
'MAIN_LANG_DEFAULT' => $langs->getDefaultLang(),
'DOL_LANG_INTERFACE_URL' => dol_buildpath('admin/tools/ui/experimental/experiments/dolibarr-context/langs-tool-interface.php', 1),
]) ?>);
</script>
<div class="doc-wrapper">
<?php $documentation->showBreadCrumb(); ?>
<div class="doc-content-wrapper">
<h1 class="documentation-title"><?php echo $langs->trans($experimentName); ?></h1>
<?php $documentation->showSummary(); ?>
<div class="documentation-section">
<h2 id="titlesection-basicusage" class="documentation-title">Introduction</h2>
<p>
The Dolibarr Context Langs Tool is a powerful JavaScript utility to manage translations and locales dynamically.<br/>
It allows you to load translation domains, set the current language, clear cache, and retrieve translated strings in your scripts.
</p>
</div>
<div class="documentation-section">
<h2 id="titlesection-setup-contextvars" class="documentation-title">Setup Context Variables</h2>
<p>
Before using the tool, you should declare the necessary context variables on your page.<br/>
These variables allow the tool to know the current Dolibarr version, the default language, and the interface URL used to fetch translations.
</p>
<p>
However, like the setEventMessage tool, the Langs tool is a core tool and is always loaded by Dolibarr.<br/>
Therefore, in most cases, you do not need to set these variables manually, as they are already defined.
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="'.getNonce().'" >',
'Dolibarr.setContextVars(<?php print json_encode([',
' \'DOL_VERSION\' => DOL_VERSION,',
' \'MAIN_LANG_DEFAULT\' => $langs->getDefaultLang(),',
' \'DOL_LANG_INTERFACE_URL\' => dol_buildpath(\'admin/tools/ui/experimental/experiments/dolibarr-context/langs-tool-interface.php\',1),',
']) ?>);',
'</script>',
);
echo $documentation->showCode($lines, 'php');
?>
</div>
</div>
<div class="documentation-section">
<h2 id="titlesection-basic-usage" class="documentation-title">Basic Usage</h2>
<p>
The main features of the Langs tool are:
</p>
<ul>
<li>Load translations for a specific domain with caching</li>
<li>Set or change the current locale</li>
<li>Clear cached translations</li>
<li>Retrieve a translated string by key</li>
</ul>
<p>Example:</p>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="'.getNonce().'" >',
'document.addEventListener(\'Dolibarr:Ready\', async function(e) {',
'',
' if(Dolibarr.checkToolExist(\'langs\')){ // not mandatory because langs tool will be a core tool',
'',
' // Load langs',
' Dolibarr.tools.langs.load(\'uxdocumentation\'); // will use cache but need to load lang in new local',
'',
' // Clear cache',
' document.getElementById(\'clearCache\').addEventListener(\'click\', async function(e) {',
' await Dolibarr.tools.langs.clearCache();',
' const txt = Dolibarr.tools.langs.trans(\'CacheCleared\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
'',
' // SET lang in fr_FR',
' document.getElementById(\'setlangFr\').addEventListener(\'click\', async function(e) {',
' await Dolibarr.tools.langs.setLocale(\'fr_FR\');',
' const txt = Dolibarr.tools.langs.trans(\'LangsLocalChangedTo\', \'fr_FR\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
'',
' // SET lang in en_US',
' document.getElementById(\'setlangEn\').addEventListener(\'click\', async function(e) {',
' await Dolibarr.tools.langs.setLocale(\'en_US\');',
' const txt = Dolibarr.tools.langs.trans(\'LangsLocalChangedTo\', \'en_US\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
'',
' // pop a message in current lang',
' document.getElementById(\'popmessage\').addEventListener(\'click\', async function(e) {',
' const txt = Dolibarr.tools.langs.trans(\'ContextLangToolTest\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
' }',
'});',
'</script>',
);
echo $documentation->showCode($lines, 'php');
print implode("\n", $lines);
?>
<p>1. Set the current lang</p>
<div >
<button id="setlangFr" class="button">Set lang in french</button>
<button id="setlangEn" class="button">Set lang in english</button>
<button id="clearCache" class="button">Clear cache</button>
</div>
<p>2. Pop translated message</p>
<div>
<button id="popmessage" class="button">pop message</button>
</div>
</div>
</div>
</div>
</div>
</div>
<?php
// Output close body + html
$documentation->docFooter();
?>

View File

@@ -165,6 +165,8 @@ ExperimentalUxContributionTxt03 = In some cases, variants may be incompatible wi
ExperimentalUxContributionEnd = This structure ensures a clear and modular organization of UX experiments, making testing and future integration more efficient. ExperimentalUxContributionEnd = This structure ensures a clear and modular organization of UX experiments, making testing and future integration more efficient.
UxDolibarrContext = JS Dolibarr context UxDolibarrContext = JS Dolibarr context
UxDolibarrContextHowItWork = How it's work
UxDolibarrContextLangsTool = Langs tool
# Start experiements menu title # Start experiements menu title
ExperimentalUxInputAjaxFeedback = Input feedback ExperimentalUxInputAjaxFeedback = Input feedback
@@ -189,3 +191,7 @@ JSDolibarrhooks = JS Dolibarr hooks
JSDolibarrAwaitHooks = JS Dolibarr Async Hooks (Await Hooks) - sequential execution JSDolibarrAwaitHooks = JS Dolibarr Async Hooks (Await Hooks) - sequential execution
ExampleOfCreatingNewContextTool = Dolibarr Tool : Example of creating a new context tool ExampleOfCreatingNewContextTool = Dolibarr Tool : Example of creating a new context tool
SetEventMessageTool = Dolibarr Tool : Set event message SetEventMessageTool = Dolibarr Tool : Set event message
ContextLangToolTest = This text is in english
LangsLocalChangedTo = Language locale changed to %s. Translations will now use the new locale.
CacheCleared = Cache cleared

View File

@@ -0,0 +1,4 @@
ContextLangToolTest = Ce texte est en français
LangsLocalChangedTo = La langue a été changée en %s. Les traductions utiliseront désormais cette nouvelle langue.
CacheCleared = Cache vidé