Skip to content

Commit

Permalink
Merge pull request #441 from blib-la/feat/add-i18next-example
Browse files Browse the repository at this point in the history
feat: add example for next-i18next
  • Loading branch information
saltyshiomix authored Apr 14, 2024
2 parents b050241 + 9aa7092 commit 205f1b0
Show file tree
Hide file tree
Showing 20 changed files with 453 additions and 0 deletions.
38 changes: 38 additions & 0 deletions examples/with-next-i18next/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<p align="center"><img src="https://i.imgur.com/X7dSE68.png"></p>

## Usage

### Create an App

```
# with npx
$ npx create-nextron-app my-app --example basic-lang-javascript
# with yarn
$ yarn create nextron-app my-app --example basic-lang-javascript
# with pnpm
$ pnpm dlx create-nextron-app my-app --example basic-lang-javascript
```

### Install Dependencies

```
$ cd my-app
# using yarn or npm
$ yarn (or `npm install`)
# using pnpm
$ pnpm install --shamefully-hoist
```

### Use it

```
# development mode
$ yarn dev (or `npm run dev` or `pnpm run dev`)
# production build
$ yarn build (or `npm run build` or `pnpm run build`)
```
12 changes: 12 additions & 0 deletions examples/with-next-i18next/electron-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
appId: com.example.nextron
productName: My Nextron App
copyright: Copyright © 2018 Yoshihide Shiono
directories:
output: dist
buildResources: resources
files:
- from: .
filter:
- package.json
- app
publish: null
46 changes: 46 additions & 0 deletions examples/with-next-i18next/main/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import path from 'path'
import { app, ipcMain } from 'electron'
import serve from 'electron-serve'

import i18next from "../next-i18next.config.js"
import { userStore } from "./helpers/user-store"
import { createWindow } from './helpers'

const isProd = process.env.NODE_ENV === 'production'

if (isProd) {
serve({ directory: 'app' })
} else {
app.setPath('userData', `${app.getPath('userData')} (development)`)
}

;(async () => {
await app.whenReady()

const mainWindow = createWindow('main', {
width: 1000,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})

const locale = userStore.get("locale", i18next.i18n.defaultLocale)
console.log("Using locale:", locale)

if (isProd) {
await mainWindow.loadURL(`app://./${locale}/home`)
} else {
const port = process.argv[2]
await mainWindow.loadURL(`http://localhost:${port}/${locale}/home`)
mainWindow.webContents.openDevTools()
}
})()

app.on('window-all-closed', () => {
app.quit()
})

ipcMain.on('message', async (event, arg) => {
event.reply('message', `${arg} World!`)
})
78 changes: 78 additions & 0 deletions examples/with-next-i18next/main/helpers/create-window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { screen, BrowserWindow } from 'electron'
import Store from 'electron-store'

export const createWindow = (windowName, options) => {
const key = 'window-state'
const name = `window-state-${windowName}`
const store = new Store({ name })
const defaultSize = {
width: options.width,
height: options.height,
}
let state = {}

const restore = () => store.get(key, defaultSize)

const getCurrentPosition = () => {
const position = win.getPosition()
const size = win.getSize()
return {
x: position[0],
y: position[1],
width: size[0],
height: size[1],
}
}

const windowWithinBounds = (windowState, bounds) => {
return (
windowState.x >= bounds.x &&
windowState.y >= bounds.y &&
windowState.x + windowState.width <= bounds.x + bounds.width &&
windowState.y + windowState.height <= bounds.y + bounds.height
)
}

const resetToDefaults = () => {
const bounds = screen.getPrimaryDisplay().bounds
return Object.assign({}, defaultSize, {
x: (bounds.width - defaultSize.width) / 2,
y: (bounds.height - defaultSize.height) / 2,
})
}

const ensureVisibleOnSomeDisplay = (windowState) => {
const visible = screen.getAllDisplays().some((display) => {
return windowWithinBounds(windowState, display.bounds)
})
if (!visible) {
// Window is partially or fully not visible now.
// Reset it to safe defaults.
return resetToDefaults()
}
return windowState
}

const saveState = () => {
if (!win.isMinimized() && !win.isMaximized()) {
Object.assign(state, getCurrentPosition())
}
store.set(key, state)
}

state = ensureVisibleOnSomeDisplay(restore())

const win = new BrowserWindow({
...state,
...options,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
...options.webPreferences,
},
})

win.on('close', saveState)

return win
}
3 changes: 3 additions & 0 deletions examples/with-next-i18next/main/helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './ipc'

export * from './create-window'
6 changes: 6 additions & 0 deletions examples/with-next-i18next/main/helpers/ipc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ipcMain } from "electron"
import { userStore } from "./user-store"

ipcMain.handle("setLocale", (_event, locale) => {
userStore.set("locale", locale)
})
3 changes: 3 additions & 0 deletions examples/with-next-i18next/main/helpers/user-store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Store from "electron-store"

export const userStore = new Store({name: "com.example.nextron"})
20 changes: 20 additions & 0 deletions examples/with-next-i18next/main/preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { contextBridge, ipcRenderer } from 'electron'

const handler = {
send(channel, value) {
ipcRenderer.send(channel, value)
},
on(channel, callback) {
const subscription = (_event, ...args) => callback(...args)
ipcRenderer.on(channel, subscription)

return () => {
ipcRenderer.removeListener(channel, subscription)
}
},
setLocale(locale) {
ipcRenderer.invoke(`setLocale`, locale)
},
}

contextBridge.exposeInMainWorld('ipc', handler)
16 changes: 16 additions & 0 deletions examples/with-next-i18next/next-i18next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/** @type {import('next-i18next').UserConfig} */
module.exports = {
i18n: {
defaultLocale: "en",
locales: [
"de",
"en",
],
},
debug: process.env.NODE_ENV === "development",
reloadOnPrerender: process.env.NODE_ENV === "development",
localePath:
typeof window === "undefined"
? require("path").resolve("./renderer/public/locales")
: "/locales",
}
29 changes: 29 additions & 0 deletions examples/with-next-i18next/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"private": true,
"name": "my-nextron-app",
"description": "My application description",
"version": "1.0.0",
"author": "Yoshihide Shiono <[email protected]>",
"contributors": [
"Gregor Adams <[email protected]>"
],
"main": "app/background.js",
"scripts": {
"dev": "nextron",
"build": "nextron build",
"postinstall": "electron-builder install-app-deps"
},
"dependencies": {
"electron-serve": "^1.3.0",
"electron-store": "^8.1.0"
},
"devDependencies": {
"electron": "29.0.0-beta.4",
"electron-builder": "^24.9.1",
"next": "^13.5.6",
"next-i18next": "15.2.0",
"nextron": "^8.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Link from "next/link"
import React, {useEffect} from "react"
import {useTranslation} from "next-i18next";
import {useRouter} from "next/router";

import i18next from "../../next-i18next.config"

const localeNames = {
de: "Deutsch",
en: "English"
}
export default function LanguageSwitcher() {
const {i18n: {language: locale}} = useTranslation()
const {pathname} = useRouter()

useEffect(() => {
window.ipc.setLocale(locale)
console.log("locale:", locale)
}, [locale])

return (
<div style={{display: "flex", gap: "0.5rem"}}>
{i18next.i18n.locales.map(locale_ => {
return (
<Link key={locale_} legacyBehavior passHref href={pathname.replace("[locale]", locale_)}>
<a>{localeNames[locale_]}</a>
</Link>
)
})}
</div>
)
}
32 changes: 32 additions & 0 deletions examples/with-next-i18next/renderer/lib/get-static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {serverSideTranslations} from "next-i18next/serverSideTranslations"

import i18next from "../../next-i18next.config.js"

export function getI18nPaths() {
return ["en", "de"].map(locale => ({
params: {
locale,
},
}))
}

export function getStaticPaths() {
return {
fallback: false, paths: getI18nPaths(),
}
}

export async function getI18nProperties(context, namespaces = ["common"]) {
const locale = context?.params?.locale ?? i18next.i18n.defaultLocale;
return {
...(await serverSideTranslations(locale, namespaces)),
}
}

export function makeStaticProperties(namespaces = []) {
return async function (context) {
return {
props: await getI18nProperties(context, namespaces),
};
};
}
7 changes: 7 additions & 0 deletions examples/with-next-i18next/renderer/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('next').NextConfig} */
module.exports = {
trailingSlash: true,
images: {
unoptimized: true,
}
}
56 changes: 56 additions & 0 deletions examples/with-next-i18next/renderer/pages/[locale]/home.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react'
import Head from 'next/head'
import Link from 'next/link'
import { useTranslation } from "next-i18next"

import LanguageSwitcher from "../../components/LanguageSwitcher"
import { getStaticPaths, makeStaticProperties } from "../../lib/get-static";

export default function HomePage() {
const {i18n: {language: locale}, t} = useTranslation()
const [message, setMessage] = React.useState(t("noMessageFound"))

React.useEffect(() => {
window.ipc.on('message', (message) => {
console.log(message)
setMessage(message)
})
}, [])

return (
<React.Fragment>
<Head>
<title>{`${t("common:home")} - Nextron (with nexti18next)`}</title>
</Head>
<div>
<p>
⚡ Electron + Next.js ⚡ -
<Link legacyBehavior passHref href={`/${locale}/next`}>
<a>{t("common:goToNext")}</a>
</Link>
</p>
<img
src="/images/logo.png"
alt="Logo image"
width="256px"
height="256px"
/>
</div>
<div>
<button
onClick={() => {
window.ipc.send('message', 'Hello')
}}
>
{t("common:testIPC")}
</button>
<p>{message}</p>
<LanguageSwitcher/>
</div>
</React.Fragment>
)
}

export const getStaticProps = makeStaticProperties(["common"]);

export {getStaticPaths};
Loading

0 comments on commit 205f1b0

Please sign in to comment.