From 438c4e229bbefb0e726d0a0d9ae0a61d6f7eb312 Mon Sep 17 00:00:00 2001 From: ridaeh Date: Sat, 7 Nov 2020 13:15:58 +0100 Subject: [PATCH] :gift: Feature: Support Muliple Languages #93 --- gatsby-node.js | 24 ++++++ package-lock.json | 48 +++++++++++ package.json | 3 + src/components/Header/Links.js | 136 +++++++++++++++--------------- src/components/Home/Hero/index.js | 12 ++- src/components/language/Link.js | 14 +++ src/i18n/i18n.js | 22 +++++ src/i18n/language-config.js | 15 ++++ src/i18n/translate.js | 29 +++++++ src/pages/index.js | 2 +- 10 files changed, 231 insertions(+), 74 deletions(-) create mode 100644 src/components/language/Link.js create mode 100644 src/i18n/i18n.js create mode 100644 src/i18n/language-config.js create mode 100644 src/i18n/translate.js diff --git a/gatsby-node.js b/gatsby-node.js index fc0cbf90..facdc318 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -5,6 +5,7 @@ const _ = require("lodash") //const PAGINATION_OFFSET = 7 const fs = require("fs") //const contributers = require("./.all-contributorsrc") +const locales = require(`./src/i18n/language-config`) const createPosts = (createPage, createRedirect, edges) => { edges.forEach(({ node }, i) => { @@ -292,3 +293,26 @@ exports.sourceNodes = async ({ actions.createNode(node) }) } + +exports.onCreatePage = ({ page, actions }) => { + const { createPage, deletePage } = actions + + deletePage(page) + + // Grab the keys of locales and map over them + Object.keys(locales).map((lang) => { + // Use the values defined in "locales" to construct the path + const localizedPath = locales[lang].is_default + ? page.path + : `${locales[lang].path}${page.path}` + + return createPage({ + ...page, + path: localizedPath, + context: { + ...page.context, + locale: lang, + }, + }) + }) +} diff --git a/package-lock.json b/package-lock.json index 146c0362..713c1d23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10789,6 +10789,14 @@ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" }, + "html-parse-stringify2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz", + "integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=", + "requires": { + "void-elements": "^2.0.1" + } + }, "html-void-elements": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", @@ -11037,6 +11045,32 @@ } } }, + "i18next": { + "version": "19.8.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.8.3.tgz", + "integrity": "sha512-eVrqAw2gGGYYJaJMYw4VM1FNFawLD4b84IsoTZMVXeWHaxAM2gyTa34j2Sip15UkBz/LrSxdFJj0Jhlrz7EvHA==", + "requires": { + "@babel/runtime": "^7.12.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, + "i18next-browser-languagedetector": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.0.1.tgz", + "integrity": "sha512-3H+OsNQn3FciomUU0d4zPFHsvJv4X66lBelXk9hnIDYDsveIgT7dWZ3/VvcSlpKk9lvCK770blRZ/CwHMXZqWw==", + "requires": { + "@babel/runtime": "^7.5.5" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -16647,6 +16681,15 @@ } } }, + "react-i18next": { + "version": "11.7.3", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.7.3.tgz", + "integrity": "sha512-7sYZqVZgdaS9Z0ZH6nuJFErCD0zz5wK3jR4/xCrWjZcxHHF3GRu7BXdicbSPprZV4ZYz7LJzxxMHO7dg5Qb70A==", + "requires": { + "@babel/runtime": "^7.3.1", + "html-parse-stringify2": "2.0.1" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -20542,6 +20585,11 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + }, "warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/package.json b/package.json index c7d62a9a..e6286d0c 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,8 @@ "gatsby-transformer-yaml": "^2.2.20", "graphql": "^14.5.8", "graphql-tag": "^2.10.1", + "i18next": "^19.8.3", + "i18next-browser-languagedetector": "^6.0.1", "isomorphic-fetch": "^2.2.1", "moment": "^2.24.0", "moment-timezone": "^0.5.27", @@ -63,6 +65,7 @@ "react-alice-carousel": "^1.17.3", "react-dom": "^16.12.0", "react-helmet": "^5.2.0", + "react-i18next": "^11.7.3", "react-modal": "^3.11.1", "react-player": "^2.1.0", "react-share": "^3.0.1", diff --git a/src/components/Header/Links.js b/src/components/Header/Links.js index 0ae25284..cb20d8a4 100644 --- a/src/components/Header/Links.js +++ b/src/components/Header/Links.js @@ -1,74 +1,78 @@ // import { ToggleMode } from "components/Mode" import React from "react" import ThemeToggle from "components/Theme/ThemeToggle" -import { Link } from "gatsby" +import { Link } from "../language/Link" import Spotify from "assets/spotify.svg" import ApplePodcast from "assets/apple-podcast.svg" import GooglePodcast from "assets/google-podcast.svg" +import { useTranslation } from "react-i18next" -export default ({ id }) => ( - <> - - Home - - - Blablas - - - Suggest Blabla - - - About - +export default ({ id }) => { + const { t, i18n } = useTranslation() + return ( + <> + + {t("Home")} + + + {t("Blablas")} + + + {t("Suggest Blabla")} + + + {t("About")} + - - - - - - - - - - {/* */} - - -) + + + + + + + + + + {/* */} + + + ) +} diff --git a/src/components/Home/Hero/index.js b/src/components/Home/Hero/index.js index 697aac59..6e7d1b0f 100644 --- a/src/components/Home/Hero/index.js +++ b/src/components/Home/Hero/index.js @@ -7,21 +7,19 @@ import HeroImage from "assets/hero.svg" import HeroImageMobile from "assets/hero_mobile.svg" import HeroImageMobileLight from "assets/hero_mobile_light.svg" import "./index.scss" - +import { useTranslation } from "react-i18next" export default () => { const { dark } = useTheme() + const { t } = useTranslation() return (
-

Hottest technology trends, In Darija!

-

- GeeksBlaBla is a community initiative, to discuss, highlight and share - the latest IT topics in Moroccan Darija. -

+

{t("Hottest technology")}

+

{t("intro")}

- Start Watching + {t("Start Watching")} {/* Suggest a topic diff --git a/src/components/language/Link.js b/src/components/language/Link.js new file mode 100644 index 00000000..6d2d91cf --- /dev/null +++ b/src/components/language/Link.js @@ -0,0 +1,14 @@ +import React from "react" +import { Link as GatsbyLink } from "gatsby" +import language_config from "../../i18n/language-config" +import { useTranslation } from "react-i18next" + +export const Link = ({ to, language, ...props }) => { + // If it's the default language, don't do anything + // If it's another language, add the "path" + const { i18n } = useTranslation() + const { is_default, path } = language_config[i18n.language] + const path_to = is_default ? to : `/${path}/${to}` + + return +} diff --git a/src/i18n/i18n.js b/src/i18n/i18n.js new file mode 100644 index 00000000..3e4c836a --- /dev/null +++ b/src/i18n/i18n.js @@ -0,0 +1,22 @@ +import i18n from "i18next" +import { initReactI18next } from "react-i18next" +import { resources } from "./translate" +import LanguageDetector from "i18next-browser-languagedetector" + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources, + fallbackLng: "en", + keySeparator: false, + interpolation: { + escapeValue: false, + }, + detection: { + order: ["path"], + lookupFromPathIndex: 0, + }, + }) + +export default i18n diff --git a/src/i18n/language-config.js b/src/i18n/language-config.js new file mode 100644 index 00000000..a05180cf --- /dev/null +++ b/src/i18n/language-config.js @@ -0,0 +1,15 @@ +module.exports = { + en: { + is_default: true, + path: "en", + display_name: "English", + locale: `en-US`, + siteLanguage: `en`, + }, + fr: { + path: "fr", + display_name: "Français", + siteLanguage: `fr`, + locale: `fr`, + }, +} diff --git a/src/i18n/translate.js b/src/i18n/translate.js new file mode 100644 index 00000000..30634ab5 --- /dev/null +++ b/src/i18n/translate.js @@ -0,0 +1,29 @@ +// the translations +// (tip move them in a JSON file and import them) +exports.resources = { + en: { + translation: { + "Hottest technology": "Hottest technology trends, In Darija!", + intro: + "GeeksBlaBla is a community initiative, to discuss, highlight and share the latest IT topics in Moroccan Darija.", + Home: "Home", + Blablas: "Blablas", + "Suggest Blabla": "Suggest Blabla", + About: "About", + "Start Watching": "Start Watching", + }, + }, + fr: { + translation: { + "Hottest technology": + "Les tendances technologiques les plus chaudes, en Darija!", + intro: + "GeeksBlaBla est une initiative communautaire visant à discuter, mettre en lumière et partager les derniers sujets liés aux technologies de l'information par la Darija marocaine.", + Home: "Accueil", + Blablas: "Blablas", + "Suggest Blabla": "Suggérer Blabla", + About: "À propos", + "Start Watching": "Commencez à regarder", + }, + }, +} diff --git a/src/pages/index.js b/src/pages/index.js index f97cc4a9..043bbffa 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,5 +1,5 @@ import React from "react" - +import "../i18n/i18n" import Layout from "components/Layout" import { Hero,