aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2021-10-26 21:54:36 +0200
committerArmand Philippot <git@armandphilippot.com>2021-10-26 21:54:36 +0200
commited9f269a78062f0d9a805b91b95fff5f479098ac (patch)
treea6794933b3994ded3dcc8992b353ec9e9c07532f
parent1d272eac38ebb310e360891a3a717447a1d0547a (diff)
feat: translate the app - two locales available: fr and en
-rw-r--r--htdocs/index.html45
-rw-r--r--htdocs/src/js/app.js27
-rw-r--r--htdocs/src/js/config/projects.js34
-rw-r--r--htdocs/src/js/i18n/i18n.js42
-rw-r--r--htdocs/src/js/i18n/locales/en.js24
-rw-r--r--htdocs/src/js/i18n/locales/fr.js24
-rw-r--r--package.json1
-rw-r--r--yarn.lock23
8 files changed, 175 insertions, 45 deletions
diff --git a/htdocs/index.html b/htdocs/index.html
index e7aaace..47e41fd 100644
--- a/htdocs/index.html
+++ b/htdocs/index.html
@@ -1,19 +1,16 @@
<!DOCTYPE html>
<html lang="en">
-
<head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Armand Philippot</title>
- <link rel="stylesheet" href="assets/css/style.css">
+ <link rel="stylesheet" href="assets/css/style.css" />
</head>
<body class="body">
<div class="toolbar">
- <button type="button" class="toolbar__options btn btn--menu">
- Menu
- </button>
+ <button type="button" class="toolbar__options btn btn--menu">Menu</button>
<button type="button" class="toolbar__options btn btn--details">
About app
</button>
@@ -22,37 +19,43 @@
<div class="branding">
<div class="branding__logo logo">
<a href="/" rel="home" class="logo__link">
- <img src="./assets/images/armand-philippot.jpg" alt="Back to homepage"
- class="logo__image logo__image--front">
- <img src="./assets/images/armand-philippot-logo.svg" alt="Back to homepage"
- class="logo__image logo__image--back">
+ <img
+ src="./assets/images/armand-philippot.jpg"
+ alt="Back to homepage"
+ class="logo__image logo__image--front"
+ />
+ <img
+ src="./assets/images/armand-philippot-logo.svg"
+ alt="Back to homepage"
+ class="logo__image logo__image--back"
+ />
</a>
</div>
<h1 class="branding__title">
<a href="/" rel="home" class="branding__link">Armand Philippot</a>
</h1>
- <p class="branding__description">
- Front-end developer
- </p>
+ <p class="branding__description">Front-end developer</p>
</div>
<nav class="nav">
<p class="nav__label">App list:</p>
- <ul class="nav__list">
- </ul>
+ <ul class="nav__list"></ul>
</nav>
</header>
<main class="main">
- <div class="instructions">Select an app inside menu to see a live preview and app details.</div>
+ <div class="instructions">
+ Select an app inside menu to see a live preview and app details.
+ </div>
</main>
<footer class="footer">
<div class="copyright">
- <span class="copyright__license">MIT</span>
+ <span class="copyright__license" title="License MIT">MIT</span>
<span class="copyright__date">2021.</span>
- <a href="https://www.armandphilippot.com/" class="copyright__author">Armand Philippot.</a>
+ <a href="https://www.armandphilippot.com/" class="copyright__author"
+ >Armand Philippot.</a
+ >
</div>
</footer>
<script src="./assets/js/runtime.js"></script>
<script src="./assets/js/app.js"></script>
</body>
-
</html>
diff --git a/htdocs/src/js/app.js b/htdocs/src/js/app.js
index b605583..2837621 100644
--- a/htdocs/src/js/app.js
+++ b/htdocs/src/js/app.js
@@ -1,4 +1,5 @@
import projects from './config/projects';
+import { translate, setLocale, currentLocale } from './i18n/i18n';
import {
hideToBottom,
hideToLeft,
@@ -134,7 +135,7 @@ function getRepos(repos) {
const list = document.createElement('ul');
const items = repos.map((repo) => getRepoItem(repo.name, repo.url));
title.classList.add('project-details__title');
- title.textContent = 'Repositories:';
+ title.textContent = translate('main.project.details.repo');
list.classList.add('list', 'list--repos');
list.append(...items);
wrapper.append(title, list);
@@ -151,7 +152,7 @@ function getTechs(technologies) {
const title = document.createElement('h3');
title.classList.add('project-details__title');
- title.textContent = 'Technologies:';
+ title.textContent = translate('main.project.details.tech');
const list = document.createElement('ul');
const items = technologies.map((technology) => {
const item = document.createElement('li');
@@ -173,17 +174,20 @@ function getProjectDetails(project) {
const title = document.createElement('h2');
const techList = project?.technologies ? getTechs(project.technologies) : [];
const reposList = getRepos(project.repo);
+ const locale = currentLocale();
let description;
if (project.description) {
description = document.createElement('p');
- description.textContent = project.description;
+ description.textContent = project.description[locale] || '';
} else {
description = '';
}
title.classList.add('project-details__title');
- title.textContent = `About ${project.name}`;
+ title.textContent = translate('main.project.details.about', {
+ name: project.name,
+ });
details.classList.add('project-details');
if (!isSmallVw()) details.classList.add('fade-in');
details.replaceChildren(title, description, ...techList, ...reposList);
@@ -228,7 +232,9 @@ function showProject(id) {
if (isSmallVw()) details.classList.add('hide');
- detailsBtn.textContent = `About ${currentProject.name}`;
+ detailsBtn.textContent = translate('main.project.details.about', {
+ name: currentProject.name,
+ });
detailsBtn.addEventListener('click', toggleProjectDetails);
window.history.pushState({}, currentProject.name, `#${id}`);
main.replaceChildren(preview, details);
@@ -311,10 +317,21 @@ function printRequestedPage() {
}
}
+function translateHTMLContent() {
+ const brandingDesc = document.querySelector('.branding__description');
+ const navLabel = document.querySelector('.nav__label');
+ const license = document.querySelector('.copyright__license');
+ brandingDesc.textContent = translate('branding.description');
+ navLabel.textContent = translate('nav.title');
+ license.title = translate('footer.license');
+}
+
/**
* Initialize the website with the projects list.
*/
function init() {
+ setLocale('en');
+ translateHTMLContent();
loadWebpackStyles();
printProjectsNav();
updateView();
diff --git a/htdocs/src/js/config/projects.js b/htdocs/src/js/config/projects.js
index bb239db..f22fa9b 100644
--- a/htdocs/src/js/config/projects.js
+++ b/htdocs/src/js/config/projects.js
@@ -2,7 +2,10 @@ const projects = [
{
id: 'bin2dec',
name: 'Bin2Dec',
- description: 'Convert a binary string to a decimal number.',
+ description: {
+ en: 'Convert a binary string to a decimal number.',
+ fr: 'Convertit un nombre binaire en un nombre décimal.',
+ },
path: './projects/js-small-apps/bin2dec/index.html',
repo: [
{
@@ -19,8 +22,10 @@ const projects = [
{
id: 'budget-app',
name: 'Budget App',
- description:
- 'By selecting a language in the initialization form, only the currency is converted (the app is not translated). Also, no data is saved on reload.',
+ description: {
+ en: 'By selecting a language in the initialization form, only the currency is converted (the app is not translated). Also, no data is saved on page reload.',
+ fr: "En sélectionnant une langue dans le formulaire d'initialisation, seul le format des nombres change (l'application n'est pas traduite). Aucune donnée n'est conservée après rechargement de la page.",
+ },
path: './projects/js-small-apps/budget-app/index.html',
repo: [
{
@@ -37,7 +42,10 @@ const projects = [
{
id: 'calculator',
name: 'Calculator',
- description: 'A basic calculator.',
+ description: {
+ en: 'A basic calculator.',
+ fr: 'Une simple calculette.',
+ },
path: './projects/js-small-apps/calculator/index.html',
repo: [
{
@@ -54,8 +62,10 @@ const projects = [
{
id: 'clock',
name: 'Clock',
- description:
- 'What time is it? You can have the current hour in three formats: an analogic clock, a numeric display or a text.',
+ description: {
+ en: 'What time is it? You can have the current hour in three formats: an analogic clock, a numeric display or a text.',
+ fr: "Quelle heure est-il ? Vous pouvez voir l'heure actuelle dans trois formats : une horloge analogique, un affichage numérique et sous forme de texte.",
+ },
path: './projects/js-small-apps/clock/index.html',
repo: [
{
@@ -72,8 +82,10 @@ const projects = [
{
id: 'css-border-previewer',
name: 'CSS Border Previewer',
- description:
- 'Play with CSS borders (style, width, border-radius). Then, you can copy the generated code if the preview suits you.',
+ description: {
+ en: 'Play with CSS borders (style, width, radius). Then, you can copy the generated code if the preview suits you.',
+ fr: "Jouez avec les bordures CSS (style, largeur, radius). Puis, vous pouvez copier le code généré si l'aperçu vous satisfait.",
+ },
path: './projects/js-small-apps/css-border-previewer/index.html',
repo: [
{
@@ -90,8 +102,10 @@ const projects = [
{
id: 'rps-game',
name: 'Rock Paper Scissors',
- description:
- 'A basic implementation of the game. Try to beat your friend or the computer.',
+ description: {
+ en: 'A basic implementation of the game. Try to beat your friend or the computer.',
+ fr: "Une implémentation du jeu. Essayez de battre votre ami ou l'ordinateur.",
+ },
path: './projects/js-small-apps/rock-paper-scissors/index.html',
repo: [
{
diff --git a/htdocs/src/js/i18n/i18n.js b/htdocs/src/js/i18n/i18n.js
new file mode 100644
index 0000000..6bdc7cd
--- /dev/null
+++ b/htdocs/src/js/i18n/i18n.js
@@ -0,0 +1,42 @@
+import I18n from 'i18n-js';
+import en from './locales/en';
+import fr from './locales/fr';
+
+const supportedLanguages = [
+ {
+ code: 'en',
+ label: 'English',
+ translations: en,
+ },
+ {
+ code: 'fr',
+ label: 'Français',
+ translations: fr,
+ },
+];
+
+supportedLanguages.forEach((locale) => {
+ I18n.translations[locale.code] = locale.translations;
+});
+
+function setLocale(locale) {
+ I18n.locale = locale;
+}
+
+function currentLocale() {
+ return I18n.currentLocale();
+}
+
+function translate(name, params = {}) {
+ return I18n.t(name, params);
+}
+
+const { defaultLocale } = I18n;
+
+export {
+ supportedLanguages,
+ setLocale,
+ translate,
+ defaultLocale,
+ currentLocale,
+};
diff --git a/htdocs/src/js/i18n/locales/en.js b/htdocs/src/js/i18n/locales/en.js
new file mode 100644
index 0000000..d8fea40
--- /dev/null
+++ b/htdocs/src/js/i18n/locales/en.js
@@ -0,0 +1,24 @@
+const en = {
+ branding: {
+ description: 'Front-end developer',
+ },
+ nav: {
+ title: 'Apps list:',
+ },
+ main: {
+ instructions:
+ 'Select an app inside menu to see a live preview and app details.',
+ project: {
+ details: {
+ about: 'About {{name}}',
+ repo: 'Repositories:',
+ tech: 'Technologies:',
+ },
+ },
+ },
+ footer: {
+ license: 'License MIT',
+ },
+};
+
+export default en;
diff --git a/htdocs/src/js/i18n/locales/fr.js b/htdocs/src/js/i18n/locales/fr.js
new file mode 100644
index 0000000..f44d459
--- /dev/null
+++ b/htdocs/src/js/i18n/locales/fr.js
@@ -0,0 +1,24 @@
+const fr = {
+ branding: {
+ description: 'Intégrateur web',
+ },
+ nav: {
+ title: 'Liste des applications :',
+ },
+ main: {
+ instructions:
+ "Sélectionnez une application dans le menu pour afficher un aperçu en direct et les informations sur l'application.",
+ project: {
+ details: {
+ about: 'À propos de {{name}}',
+ repo: 'Dépôts :',
+ tech: 'Technologies :',
+ },
+ },
+ },
+ footer: {
+ license: 'Licence MIT',
+ },
+};
+
+export default fr;
diff --git a/package.json b/package.json
index f1dad95..fa680a2 100644
--- a/package.json
+++ b/package.json
@@ -67,6 +67,7 @@
"@babel/runtime": "^7.15.4",
"dotenv": "^10.0.0",
"dotenv-expand": "^5.1.0",
+ "i18n-js": "^3.8.0",
"modern-normalize": "^1.1.0"
}
}
diff --git a/yarn.lock b/yarn.lock
index 65a5612..c5ab4d9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1202,9 +1202,9 @@
integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==
"@types/node@*":
- version "16.11.5"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.5.tgz#e91be5ba4ab88c06095e7b61f9ad1767a1093faf"
- integrity sha512-NyUV2DGcqYIx9op++MG2+Z4Nhw1tPhi0Wfs81TgncuX1aJC4zf2fgCJlJhl4BW9bCSS04e34VkqmOS96w0XQdg==
+ version "16.11.6"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
+ integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
@@ -2929,9 +2929,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.878:
- version "1.3.878"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.878.tgz#baa9fb5c24b9b580f08fb245cbb52a22f8fc8fa8"
- integrity sha512-O6yxWCN9ph2AdspAIszBnd9v8s11hQx8ub9w4UGApzmNRnoKhbulOWqbO8THEQec/aEHtvy+donHZMlh6l1rbA==
+ version "1.3.879"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.879.tgz#4aba9700cfb241fb95c6ed69e31785e3d1605a43"
+ integrity sha512-zJo+D9GwbJvM31IdFmwcGvychhk4KKbKYo2GWlsn+C/dxz2NwmbhGJjWwTfFSF2+eFH7VvfA8MCZ8SOqTrlnpw==
emoji-regex@^8.0.0:
version "8.0.0"
@@ -4150,6 +4150,11 @@ husky@^7.0.2:
resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535"
integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
+i18n-js@^3.8.0:
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/i18n-js/-/i18n-js-3.8.0.tgz#b8fd6b12e1d88cb71f9806c29bca7c31c012e504"
+ integrity sha512-hDsGgPuvw/2P+lXSbOafAwspK8Ste8YrwuuUg17W3wEcO1JkQxBlPgsN1t2+852nTnz4YSYTjZc/1nAA2PC/nw==
+
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -4881,9 +4886,9 @@ lines-and-columns@^1.1.6:
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
lint-staged@^11.2.3:
- version "11.2.4"
- resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.2.4.tgz#29bc84e0318e6e9d4a7a043513236d066e39ee1d"
- integrity sha512-aTUqcPDSV05EyKlMT4N5h7tmnevKfAxI3xZkRb+DHfmcFaoCxfxVvpqlLMCVGy3EYle9JYER2nA5zc4eNTkZVQ==
+ version "11.2.5"
+ resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.2.5.tgz#afa4c4740fbf874fab1531a846f14504e448e828"
+ integrity sha512-bD6FeKyRtMA3jXcNg/J0fuJmwDN8yaoTdqliFcEl8pFTHiP9NYwQCeZwBoZ8FEXx4lj/pli3oFehr86XYvMNEA==
dependencies:
cli-truncate "2.1.0"
colorette "^1.4.0"