├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── README.md ├── assets ├── README.md └── buefy.png ├── components ├── LanguageSwitcher.vue └── README.md ├── i18n.config.js ├── layouts ├── README.md └── default.vue ├── middleware └── README.md ├── nuxt.config.js ├── package.json ├── pages ├── README.md ├── blog │ └── _slug.vue └── index.vue ├── plugins ├── README.md ├── ctx-inject.js └── filters.js ├── services └── index.js ├── static ├── README.md └── favicon.ico ├── store ├── README.md └── index.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: 'babel-eslint' 9 | }, 10 | extends: [ 11 | '@nuxtjs', 12 | 'plugin:nuxt/recommended' 13 | ], 14 | // add your custom rules here 15 | rules: { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | 62 | # parcel-bundler cache (https://parceljs.org/) 63 | .cache 64 | 65 | # next.js build output 66 | .next 67 | 68 | # nuxt.js build output 69 | .nuxt 70 | 71 | # Nuxt generate 72 | dist 73 | 74 | # vuepress build output 75 | .vuepress/dist 76 | 77 | # Serverless directories 78 | .serverless 79 | 80 | # IDE / Editor 81 | .idea 82 | 83 | # Service worker 84 | sw.* 85 | 86 | # macOS 87 | .DS_Store 88 | 89 | # Vim swap files 90 | *.swp 91 | 92 | /ARTICLE.md 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JAMStack multilanguage blog with Nuxt.js 2 | 3 | 📝 Use Nuxt.js + Strapi to build a JAMStack multilanguage blog! In [this tutorial](https://dev.to/astagi/how-to-build-a-jamstack-multi-language-blog-with-nuxt-js-3gah) you'll learn how to do! 4 | 5 | [![Netlify Status](https://api.netlify.com/api/v1/badges/98739ae5-8f73-4f67-be35-d5c71aae91fa/deploy-status)](https://app.netlify.com/sites/eager-shockley-a415b7/deploys) 6 | 7 | ## Build Setup 8 | 9 | ```bash 10 | # install dependencies 11 | $ yarn install 12 | 13 | # serve with hot reload at localhost:3000 14 | $ yarn dev 15 | 16 | # build for production and launch server 17 | $ yarn build 18 | 19 | # generate full static site 20 | $ yarn export 21 | 22 | # serve 23 | 24 | yarn serve 25 | ``` 26 | 27 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 28 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /assets/buefy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astagi/nuxt-strapi-i18n/2bcfc4abdc39f16b35974966043c24a9af282f24/assets/buefy.png -------------------------------------------------------------------------------- /components/LanguageSwitcher.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /components/README.md: -------------------------------------------------------------------------------- 1 | # COMPONENTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | The components directory contains your Vue.js Components. 6 | 7 | _Nuxt.js doesn't supercharge these components._ 8 | -------------------------------------------------------------------------------- /i18n.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | messages: { 3 | en: { 4 | readmore: 'Read more', 5 | author: 'Author', 6 | published: 'Published on' 7 | }, 8 | es: { 9 | readmore: 'Lee mas', 10 | author: 'Autor', 11 | published: 'Publicado el' 12 | }, 13 | it: { 14 | readmore: 'Leggi di più', 15 | author: 'Autore', 16 | published: 'Pubblicato il' 17 | } 18 | }, 19 | pages: { 20 | 'blog/_slug': { 21 | it: '/articoli/:slug', 22 | es: '/artículos/:slug', 23 | en: '/blog/:slug' 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 49 | 50 | 55 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | import BlogClient from './services' 2 | import i18nConfig from './i18n.config' 3 | 4 | const LOCALES = [ 5 | { 6 | code: 'en', 7 | iso: 'en-US' 8 | }, 9 | { 10 | code: 'es', 11 | iso: 'es-ES' 12 | }, 13 | { 14 | code: 'it', 15 | iso: 'it-IT' 16 | } 17 | ] 18 | const DEFAULT_LOCALE = 'en' 19 | const BASE_URL = process.env.NUXT_ENV_BASE_URL || 'http://localhost:3000' 20 | 21 | export default { 22 | mode: 'universal', 23 | target: 'static', 24 | /* 25 | ** Headers of the page 26 | */ 27 | head: { 28 | title: 'My JAMBlog', 29 | meta: [ 30 | { charset: 'utf-8' }, 31 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 32 | { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }, 33 | { hid: 'og:title', name: 'og:title', content: 'My JAMBlog' }, 34 | { hid: 'og:image', name: 'og:image', content: 'https://pngimg.com/uploads/jam/jam_PNG93.png' } 35 | ], 36 | link: [ 37 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } 38 | ] 39 | }, 40 | /* 41 | ** Customize the progress-bar color 42 | */ 43 | loading: { color: '#fff' }, 44 | /* 45 | ** Global CSS 46 | */ 47 | css: [ 48 | ], 49 | /* 50 | ** Plugins to load before mounting the App 51 | */ 52 | plugins: [ 53 | '~/plugins/ctx-inject.js', 54 | '~/plugins/filters.js' 55 | ], 56 | /* 57 | ** Nuxt.js dev-modules 58 | */ 59 | buildModules: [ 60 | // Doc: https://github.com/nuxt-community/eslint-module 61 | '@nuxtjs/eslint-module' 62 | ], 63 | /* 64 | ** Nuxt.js modules 65 | */ 66 | modules: [ 67 | // Doc: https://nuxt-community.github.io/nuxt-i18n/ 68 | 'nuxt-i18n', 69 | // Doc: https://buefy.github.io/#/documentation 70 | 'nuxt-buefy', 71 | // Doc: https://axios.nuxtjs.org/usage 72 | '@nuxtjs/axios', 73 | // Doc: https://github.com/nuxt-community/sitemap-module 74 | '@nuxtjs/sitemap' 75 | ], 76 | /* 77 | ** Axios module configuration 78 | ** See https://axios.nuxtjs.org/options 79 | */ 80 | axios: { 81 | }, 82 | /* 83 | ** Build configuration 84 | */ 85 | build: { 86 | /* 87 | ** You can extend webpack config here 88 | */ 89 | extend (config, ctx) { 90 | } 91 | }, 92 | i18n: { 93 | baseUrl: BASE_URL, 94 | locales: LOCALES, 95 | defaultLocale: DEFAULT_LOCALE, 96 | parsePages: false, 97 | pages: i18nConfig.pages, 98 | encodePaths: false, 99 | seo: false, 100 | vueI18n: { 101 | fallbackLocale: DEFAULT_LOCALE, 102 | messages: i18nConfig.messages 103 | } 104 | }, 105 | generate: { 106 | crawler: false, 107 | routes: async () => { 108 | const client = new BlogClient() 109 | let routes = [] 110 | let postsData = [] 111 | for (const locale of LOCALES) { 112 | postsData = await client.getAllPosts(locale.code) 113 | routes = routes.concat(postsData.data.transPosts.map((post) => { 114 | return { 115 | route: `${locale.code === DEFAULT_LOCALE ? '' : '/' + locale.code}${i18nConfig.pages['blog/_slug'][locale.code].replace(':slug', post.slug)}`, 116 | payload: post 117 | } 118 | })) 119 | } 120 | return routes 121 | } 122 | }, 123 | sitemap: { 124 | hostname: BASE_URL, 125 | gzip: true, 126 | i18n: DEFAULT_LOCALE 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multilangblog", 3 | "version": "1.0.0", 4 | "description": "My wonderful JAMBlog", 5 | "author": "Andrea Stagi", 6 | "private": true, 7 | "scripts": { 8 | "dev": "nuxt", 9 | "build": "nuxt build", 10 | "start": "nuxt start", 11 | "export": "nuxt export", 12 | "serve": "nuxt serve", 13 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore ." 14 | }, 15 | "dependencies": { 16 | "@nuxtjs/axios": "^5.3.6", 17 | "@nuxtjs/sitemap": "^2.3.0", 18 | "apollo-fetch": "^0.7.0", 19 | "babel-runtime": "^6.26.0", 20 | "nuxt": "^2.13.0", 21 | "nuxt-buefy": "^0.3.2", 22 | "nuxt-i18n": "^6.11.1", 23 | "nuxt-material-design-icons": "^1.0.4", 24 | "vue-markdown": "^2.2.4" 25 | }, 26 | "devDependencies": { 27 | "@nuxtjs/eslint-config": "^2.0.0", 28 | "@nuxtjs/eslint-module": "^1.0.0", 29 | "babel-eslint": "^10.0.1", 30 | "eslint": "^6.1.0", 31 | "eslint-plugin-nuxt": ">=0.4.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /pages/blog/_slug.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 74 | 83 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 35 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /plugins/ctx-inject.js: -------------------------------------------------------------------------------- 1 | import BlogClient from '~/services' 2 | 3 | export default ({ app }, inject) => { 4 | app.$blogClient = new BlogClient() 5 | } 6 | -------------------------------------------------------------------------------- /plugins/filters.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.filter( 4 | 'dataFromTimestamp', 5 | (val, locale) => new Date(val).toLocaleDateString(locale) 6 | ) 7 | 8 | Vue.filter( 9 | 'serverAbsoluteUrl', 10 | url => `${process.env.NUXT_ENV_BACKEND_URL}${url}` 11 | ) 12 | -------------------------------------------------------------------------------- /services/index.js: -------------------------------------------------------------------------------- 1 | import { createApolloFetch } from 'apollo-fetch' 2 | 3 | export default class BlogClient { 4 | constructor () { 5 | this.apolloFetch = createApolloFetch({ uri: `${process.env.NUXT_ENV_BACKEND_URL}/graphql` }) 6 | } 7 | 8 | getAllPostsHead (lang) { 9 | const allPostsQuery = ` 10 | query AllPosts($lang: String!) { 11 | transPosts(where: {lang: $lang}) { 12 | slug 13 | title, 14 | post { 15 | published 16 | author { 17 | complete_name 18 | } 19 | } 20 | } 21 | } 22 | ` 23 | return this.apolloFetch({ 24 | query: allPostsQuery, 25 | variables: { 26 | lang 27 | } 28 | }) 29 | } 30 | 31 | getAllPosts (lang) { 32 | const allPostsQuery = ` 33 | query AllPosts($lang: String!) { 34 | transPosts(where: {lang: $lang}) { 35 | slug 36 | title 37 | subtitle 38 | content 39 | cover { 40 | url 41 | } 42 | post { 43 | published 44 | author { 45 | complete_name 46 | image { 47 | url 48 | } 49 | } 50 | transPosts(where: {lang_ne: $lang}) { 51 | slug 52 | lang 53 | } 54 | } 55 | } 56 | } 57 | ` 58 | return this.apolloFetch({ 59 | query: allPostsQuery, 60 | variables: { 61 | lang 62 | } 63 | }) 64 | } 65 | 66 | getSinglePost (slug, lang) { 67 | const simplePostQuery = ` 68 | query Post($slug: String!, $lang: String!) { 69 | transPosts(where: {slug : $slug, lang: $lang}) { 70 | slug 71 | title 72 | subtitle 73 | content 74 | cover { 75 | url 76 | } 77 | post { 78 | published 79 | author { 80 | complete_name, 81 | image { 82 | url 83 | } 84 | } 85 | transPosts(where: {lang_ne: $lang}) { 86 | slug 87 | lang 88 | } 89 | } 90 | } 91 | } 92 | ` 93 | return this.apolloFetch({ 94 | query: simplePostQuery, 95 | variables: { 96 | slug, 97 | lang 98 | } 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astagi/nuxt-strapi-i18n/2bcfc4abdc39f16b35974966043c24a9af282f24/static/favicon.ico -------------------------------------------------------------------------------- /store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /store/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astagi/nuxt-strapi-i18n/2bcfc4abdc39f16b35974966043c24a9af282f24/store/index.js --------------------------------------------------------------------------------