major: i18n concept implementation
This commit is contained in:
parent
2ec570f67d
commit
2506b5fad7
1107
package-lock.json
generated
1107
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@ -12,19 +12,31 @@
|
|||||||
"build:js": "babel \"src/scripts/*.js\" -o dist/scripts/index.js",
|
"build:js": "babel \"src/scripts/*.js\" -o dist/scripts/index.js",
|
||||||
"build:jscomponents": "babel \"src/components/*.js\" --out-dir dist/components/",
|
"build:jscomponents": "babel \"src/components/*.js\" --out-dir dist/components/",
|
||||||
"build:html": "pug src/index.pug -o dist/ --obj config.json",
|
"build:html": "pug src/index.pug -o dist/ --obj config.json",
|
||||||
|
"build:i18n": "copyfiles -u 2 src/locales/*.json dist/locales",
|
||||||
"copy:font": "copyfiles -f src/fonts/* dist/fonts",
|
"copy:font": "copyfiles -f src/fonts/* dist/fonts",
|
||||||
"copy:image": "copyfiles -f src/images/* dist/images",
|
"copy:image": "copyfiles -f src/images/* dist/images",
|
||||||
"serve": "pushstate-server -d ./dist -p 3000",
|
"serve": "pushstate-server -d ./dist -p 3000",
|
||||||
"watch": "run-p copy:* watch:* serve",
|
"watch": "run-p build watch:* serve",
|
||||||
"watch:css": "npm run build:css && npm run build:css -- -w",
|
"watch:css": "npm-watch build:css",
|
||||||
"watch:js": "babel --watch \"src/scripts/*.js\" -o dist/scripts/index.js",
|
"watch:js": "npm-watch build:js",
|
||||||
"watch:jscomponents": "babel --watch \"src/components/*.js\" --out-dir dist/components/",
|
"watch:jscomponents": "npm-watch build:jscomponents",
|
||||||
"watch:pug": "pug --watch src/index.pug -o dist/ --obj config.json",
|
"watch:pug": "pug --watch src/index.pug -o dist/ --obj config.json",
|
||||||
|
"watch:i18n": "npm-watch build:i18n",
|
||||||
|
"watch:font": "npm-watch copy:font",
|
||||||
|
"watch:image": "npm-watch copy:image",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"cypress:verify": "cypress verify",
|
"cypress:verify": "cypress verify",
|
||||||
"cypress:info": "cypress info",
|
"cypress:info": "cypress info",
|
||||||
"test": "cypress run"
|
"test": "cypress run"
|
||||||
},
|
},
|
||||||
|
"watch": {
|
||||||
|
"build:css": "src/styles/*",
|
||||||
|
"build:i18n": "src/locales/*",
|
||||||
|
"copy:images": "src/fonts/*.js",
|
||||||
|
"copy:fonts": "src/images/*.js",
|
||||||
|
"build:js": "src/scripts/*.js",
|
||||||
|
"build:jscomponents": "src/components/*.js"
|
||||||
|
},
|
||||||
"release": {
|
"release": {
|
||||||
"branches": [
|
"branches": [
|
||||||
"master"
|
"master"
|
||||||
@ -69,6 +81,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cypress": "^4.5.0",
|
"cypress": "^4.5.0",
|
||||||
"cypress-localstorage-commands": "^1.2.1",
|
"cypress-localstorage-commands": "^1.2.1",
|
||||||
"cypress-terminal-report": "^1.2.1"
|
"cypress-terminal-report": "^1.2.1",
|
||||||
|
"npm-watch": "^0.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,11 @@ if (endpoints.resources || (endpoints.get && endpoints.get.resources)) && (endpo
|
|||||||
//- script(type="module" src="/lib/sib-resource/sib-resource.js" defer)
|
//- script(type="module" src="/lib/sib-resource/sib-resource.js" defer)
|
||||||
|
|
||||||
if endpoints.joboffers || (endpoints.get && endpoints.get.joboffers)
|
if endpoints.joboffers || (endpoints.get && endpoints.get.joboffers)
|
||||||
script(type="module" src="https://unpkg.com/@startinblox/component-job-board@0.9" defer)
|
script(type="module" src="https://unpkg.com/@startinblox/component-job-board@1.0" defer)
|
||||||
//- script(type="module" src="/lib/solid-job-board/dist/index.js" defer)
|
//- script(type="module" src="/lib/solid-job-board/dist/index.js" defer)
|
||||||
|
|
||||||
if (endpoints.uploads || (endpoints.get && endpoints.get.uploads)) && (endpoints.skills || (endpoints.get && endpoints.get.skills)) && (endpoints.users || (endpoints.get && endpoints.get.users))
|
if (endpoints.uploads || (endpoints.get && endpoints.get.uploads)) && (endpoints.skills || (endpoints.get && endpoints.get.skills)) && (endpoints.users || (endpoints.get && endpoints.get.users))
|
||||||
script(type="module" src="https://unpkg.com/@startinblox/component-directory@0.10" defer)
|
script(type="module" src="https://unpkg.com/@startinblox/component-directory@1.0" defer)
|
||||||
//- script(type="module" src="/lib/solid-directory/dist/index.js" defer)
|
//- script(type="module" src="/lib/solid-directory/dist/index.js" defer)
|
||||||
|
|
||||||
if endpoints.dashboards || (endpoints.get && endpoints.get.dashboards)
|
if endpoints.dashboards || (endpoints.get && endpoints.get.dashboards)
|
||||||
|
20
src/locales/en.json
Normal file
20
src/locales/en.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"menuLeft": {
|
||||||
|
"emptyCircleProject": {
|
||||||
|
"notPartOf": "Tu ne fais partie d'aucun",
|
||||||
|
"createNew": "Pour en créer un nouveau, tu peux te rendre dans le",
|
||||||
|
"adminPanel": "panneau d'administration",
|
||||||
|
"project": "projet",
|
||||||
|
"circle": "cercle"
|
||||||
|
},
|
||||||
|
"dashboard": "Tableau de bord",
|
||||||
|
"events": "Evènements",
|
||||||
|
"gov": "Gouvernance",
|
||||||
|
"resources": "Ressources",
|
||||||
|
"jobBoard": "Offres de mission",
|
||||||
|
"profileDirectory": "Annuaire des membres",
|
||||||
|
"projects": "Projets",
|
||||||
|
"circles": "Circles",
|
||||||
|
"messages": "Messages"
|
||||||
|
}
|
||||||
|
}
|
@ -14,10 +14,13 @@ solid-widget(name='hubl-menu-publicprivate')
|
|||||||
|
|
||||||
solid-widget(name='hubl-create')
|
solid-widget(name='hubl-create')
|
||||||
template
|
template
|
||||||
p.create Tu ne fais partie d'aucun ${value}.
|
p.create
|
||||||
|
span(data-trans="menuLeft.emptyCircleProject.notPartOf")
|
||||||
|
span ${value}.
|
||||||
br
|
br
|
||||||
| Pour en créer un nouveau, tu peux te rendre dans le
|
span(data-trans="menuLeft.emptyCircleProject.createNew")
|
||||||
solid-link(next='admin') panneau d'administration
|
span
|
||||||
|
solid-link(next='admin', data-trans="menuLeft.emptyCircleProject.adminPanel")
|
||||||
|
|
||||||
solid-widget(name='hubl-menu-fix-url-circle')
|
solid-widget(name='hubl-menu-fix-url-circle')
|
||||||
template
|
template
|
||||||
@ -53,32 +56,32 @@ solid-widget(name='hubl-menu-fix-url-project')
|
|||||||
solid-router#navbar-router(default-route='dashboard')
|
solid-router#navbar-router(default-route='dashboard')
|
||||||
if endpoints.dashboards || (endpoints.get && endpoints.get.dashboards)
|
if endpoints.dashboards || (endpoints.get && endpoints.get.dashboards)
|
||||||
solid-route.menu(name='dashboard')
|
solid-route.menu(name='dashboard')
|
||||||
div.menu-label Tableau de bord
|
div.menu-label(data-trans="menuLeft.dashboard")
|
||||||
div.menu-icon.icon-home
|
div.menu-icon.icon-home
|
||||||
div.divider
|
div.divider
|
||||||
if publicDirectory && (endpoints.users || (endpoints.get && endpoints.get.users))
|
if publicDirectory && (endpoints.users || (endpoints.get && endpoints.get.users))
|
||||||
solid-route.menu(name='members')
|
solid-route.menu(name='members')
|
||||||
div.menu-label Annuaire des membres
|
div.menu-label(data-trans="menuLeft.profileDirectory")
|
||||||
div.menu-icon.icon-people
|
div.menu-icon.icon-people
|
||||||
div.divider
|
div.divider
|
||||||
if endpoints.joboffers || (endpoints.get && endpoints.get.joboffers)
|
if endpoints.joboffers || (endpoints.get && endpoints.get.joboffers)
|
||||||
solid-route.menu(name='job-offers', rdf-type='hd:joboffer')
|
solid-route.menu(name='job-offers', rdf-type='hd:joboffer')
|
||||||
div.menu-label Offres de mission
|
div.menu-label(data-trans="menuLeft.jobBoard")
|
||||||
div.menu-icon.icon-briefcase
|
div.menu-icon.icon-briefcase
|
||||||
div.divider
|
div.divider
|
||||||
if endpoints.resources || (endpoints.get && endpoints.get.resources)
|
if endpoints.resources || (endpoints.get && endpoints.get.resources)
|
||||||
solid-route.menu(name='resources')
|
solid-route.menu(name='resources')
|
||||||
div.menu-label Ressources
|
div.menu-label(data-trans="menuLeft.resources")
|
||||||
div.menu-icon.icon-docs
|
div.menu-icon.icon-docs
|
||||||
div.divider
|
div.divider
|
||||||
if endpoints.polls || (endpoints.get && endpoints.get.polls)
|
if endpoints.polls || (endpoints.get && endpoints.get.polls)
|
||||||
solid-route.menu(name='polls')
|
solid-route.menu(name='polls')
|
||||||
div.menu-label Gouvernance
|
div.menu-label(data-trans="menuLeft.gov")
|
||||||
div.menu-icon.icon-bubbles
|
div.menu-icon.icon-bubbles
|
||||||
div.divider
|
div.divider
|
||||||
if endpoints.events || (endpoints.get && endpoints.get.events)
|
if endpoints.events || (endpoints.get && endpoints.get.events)
|
||||||
solid-route.menu(name='events')
|
solid-route.menu(name='events')
|
||||||
div.menu-label Evènements
|
div.menu-label(data-trans="menuLeft.events")
|
||||||
div.menu-icon.icon-calendar
|
div.menu-icon.icon-calendar
|
||||||
div.divider
|
div.divider
|
||||||
if endpoints.projects || (endpoints.get && endpoints.get.projects)
|
if endpoints.projects || (endpoints.get && endpoints.get.projects)
|
||||||
@ -89,7 +92,7 @@ solid-router#navbar-router(default-route='dashboard')
|
|||||||
div.menu-chevron
|
div.menu-chevron
|
||||||
div.menu-icon.icon-arrow-up
|
div.menu-icon.icon-arrow-up
|
||||||
//- div.menu-icon.icon-arrow-right-circle
|
//- div.menu-icon.icon-arrow-right-circle
|
||||||
div.menu-label Projets
|
div.menu-label(data-trans="menuLeft.projects")
|
||||||
div.menu-icon.icon-folder-alt
|
div.menu-icon.icon-folder-alt
|
||||||
solid-route(name='project', rdf-type='hd:project', use-id='', hidden)
|
solid-route(name='project', rdf-type='hd:project', use-id='', hidden)
|
||||||
div.sub-menu.menu-notification
|
div.sub-menu.menu-notification
|
||||||
@ -104,7 +107,8 @@ solid-router#navbar-router(default-route='dashboard')
|
|||||||
fields='project'
|
fields='project'
|
||||||
loader-id='loader-projects'
|
loader-id='loader-projects'
|
||||||
empty-widget='hubl-create'
|
empty-widget='hubl-create'
|
||||||
empty-value='projet'
|
empty-value=''
|
||||||
|
data-trans="empty-value=menuLeft.emptyCircleProject.project"
|
||||||
widget-project='hubl-menu-fix-url-project'
|
widget-project='hubl-menu-fix-url-project'
|
||||||
order-by="project.customer.name"
|
order-by="project.customer.name"
|
||||||
)
|
)
|
||||||
@ -117,7 +121,7 @@ solid-router#navbar-router(default-route='dashboard')
|
|||||||
div.menu-chevron
|
div.menu-chevron
|
||||||
div.menu-icon.icon-arrow-up
|
div.menu-icon.icon-arrow-up
|
||||||
//- div.menu-icon.icon-arrow-right-circle
|
//- div.menu-icon.icon-arrow-right-circle
|
||||||
div.menu-label Cercles
|
div.menu-label(data-trans="menuLeft.circles")
|
||||||
div.menu-icon.icon-folder-alt
|
div.menu-icon.icon-folder-alt
|
||||||
solid-route(name='circle', rdf-type='hd:circle', use-id='', hidden)
|
solid-route(name='circle', rdf-type='hd:circle', use-id='', hidden)
|
||||||
div.sub-menu.menu-notification
|
div.sub-menu.menu-notification
|
||||||
@ -132,7 +136,8 @@ solid-router#navbar-router(default-route='dashboard')
|
|||||||
fields='circle'
|
fields='circle'
|
||||||
loader-id='loader-circles'
|
loader-id='loader-circles'
|
||||||
empty-widget='hubl-create'
|
empty-widget='hubl-create'
|
||||||
empty-value='cercle'
|
empty-value=''
|
||||||
|
data-trans="empty-value=menuLeft.emptyCircleProject.circle"
|
||||||
widget-circle='hubl-menu-fix-url-circle'
|
widget-circle='hubl-menu-fix-url-circle'
|
||||||
order-by="circle.name"
|
order-by="circle.name"
|
||||||
)
|
)
|
||||||
@ -142,7 +147,7 @@ solid-router#navbar-router(default-route='dashboard')
|
|||||||
div.menu
|
div.menu
|
||||||
div.menu-chevron
|
div.menu-chevron
|
||||||
div.menu-icon.icon-arrow-up
|
div.menu-icon.icon-arrow-up
|
||||||
div.menu-label Messages
|
div.menu-label(data-trans="menuLeft.messages")
|
||||||
div.menu-icon.icon-envelope-letter
|
div.menu-icon.icon-envelope-letter
|
||||||
solid-route(name='messages', rdf-type='foaf:user', use-id='', hidden)
|
solid-route(name='messages', rdf-type='foaf:user', use-id='', hidden)
|
||||||
div.sub-menu.menu-notification
|
div.sub-menu.menu-notification
|
||||||
|
178
src/scripts/intl.js
Normal file
178
src/scripts/intl.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
js intl, inspired by danabr/jsI18n
|
||||||
|
*/
|
||||||
|
class JsI18n {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.locale = ""; //Current locale
|
||||||
|
this.locales = new Array(); //Available locales
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Method for automatically detecting the language, does not work in every browser.
|
||||||
|
*/
|
||||||
|
detectLanguage() {
|
||||||
|
if (localStorage.getItem('language') || (window.navigator.language !== null && window.navigator.language !== undefined)) {
|
||||||
|
this.setLocale(localStorage.getItem('language') || window.navigator.language.slice(0, 2));
|
||||||
|
} else {
|
||||||
|
console.error('Language not found');
|
||||||
|
this.setLocale('en');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Translates tag contents and
|
||||||
|
attributes depending on the
|
||||||
|
value of key.
|
||||||
|
*/
|
||||||
|
translateTag(node, key) {
|
||||||
|
if (key.indexOf("=") == -1) { //Simple key
|
||||||
|
this.translateNodeContent(node, key);
|
||||||
|
} else { //Attribute/key pairs
|
||||||
|
var parts = key.split(";");
|
||||||
|
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
var pair = parts[i].split("=");
|
||||||
|
var attr = pair[0].toLowerCase().trim();
|
||||||
|
var k = pair[1].trim();
|
||||||
|
|
||||||
|
if (attr == "html") {
|
||||||
|
this.translateNodeContent(node, k);
|
||||||
|
} else {
|
||||||
|
this.translateNodeContent(node.attributes[attr], k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Replace the content of the given node
|
||||||
|
if there is a translation for the given key.
|
||||||
|
**/
|
||||||
|
translateNodeContent(node, key) {
|
||||||
|
var translation = this.t(key);
|
||||||
|
if (node != null && translation != undefined) {
|
||||||
|
if (node.nodeType == 1) { //Element
|
||||||
|
try {
|
||||||
|
node.innerHTML = translation;
|
||||||
|
} catch (e) {
|
||||||
|
node.text = translation;
|
||||||
|
}
|
||||||
|
} else if (node.nodeType == 2) { //Attribute
|
||||||
|
node.value = translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Helper for translating a node
|
||||||
|
and all its child nodes.
|
||||||
|
*/
|
||||||
|
processNode(node) {
|
||||||
|
if (node != undefined) {
|
||||||
|
if (node.nodeType == 1) { //Element node
|
||||||
|
|
||||||
|
var key = node.attributes["data-trans"];
|
||||||
|
if (key != null) {
|
||||||
|
this.translateTag(node, key.nodeValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Process child nodes
|
||||||
|
var children = node.childNodes;
|
||||||
|
for (var i = 0; i < children.length; i++) {
|
||||||
|
this.processNode(children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Adds a locale to the list,
|
||||||
|
replacing the translations
|
||||||
|
if the locale is already defined.
|
||||||
|
*/
|
||||||
|
addLocale(locale, translations) {
|
||||||
|
this.locales[locale.toString()] = translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sets the locale to use when translating.
|
||||||
|
*/
|
||||||
|
setLocale(locale) {
|
||||||
|
try {
|
||||||
|
fetch(`/locales/${locale}.json`).then((result) => {
|
||||||
|
if (result.ok) {
|
||||||
|
result.json().then(e => {
|
||||||
|
this.addLocale(locale, e);
|
||||||
|
this.processPage();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.locale = locale;
|
||||||
|
} catch {
|
||||||
|
if (locale != "en") {
|
||||||
|
console.warn(`Locale not found: ${locale}, fallback to english`);
|
||||||
|
this.setLocale("en");
|
||||||
|
} else {
|
||||||
|
console.error("Language not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fetches the translation associated with the given key.
|
||||||
|
*/
|
||||||
|
t(key) {
|
||||||
|
var translations = this.locales[this.locale];
|
||||||
|
if (translations != undefined) {
|
||||||
|
let translation = key.toString().split('.').reduce((o, i) => (o ? o[i] : undefined), translations);
|
||||||
|
if(typeof translation == "string") {
|
||||||
|
return translation;
|
||||||
|
} else {
|
||||||
|
return translations[key.toString()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Alias for JsI18n.t
|
||||||
|
*/
|
||||||
|
translate(key) {
|
||||||
|
this.t(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Replaces the contents of all tags
|
||||||
|
that have the data-trans attribute set.
|
||||||
|
**/
|
||||||
|
processPage() {
|
||||||
|
this.processNode(document.getElementsByTagName("html")[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Global
|
||||||
|
jsI18n = new JsI18n;
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
|
||||||
|
// Detect the lang & initialize, based on the browser or "language" item from localstorage
|
||||||
|
jsI18n.detectLanguage();
|
||||||
|
|
||||||
|
/*
|
||||||
|
recursivePopulate(DOMElement)
|
||||||
|
Will listen for the populate event of any sib element
|
||||||
|
Process the changed node every time it populate
|
||||||
|
Recursively add a populate listener for children elements
|
||||||
|
*/
|
||||||
|
function recursivePopulate(element) {
|
||||||
|
Array.from(element.querySelectorAll('*')).forEach((e) => {
|
||||||
|
e.addEventListener("populate", (e) => {
|
||||||
|
recursivePopulate(e.target);
|
||||||
|
jsI18n.processNode(e.target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Process every children from document
|
||||||
|
recursivePopulate(document);
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user