Skip to content

Commit

Permalink
Merge branch 'localization'
Browse files Browse the repository at this point in the history
  • Loading branch information
saplinganon committed Mar 19, 2023
2 parents daa6f0e + d6d1a6a commit 85edd86
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 82 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
/.yarn

# testing
/coverage
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,13 @@ Run either init_postgres.sql or init_sqlite3.sql on your database to create the
## How to add images

1. Put a .png or .jpg file in public/imagesets/[name]

## How to add languages

1. Open `next.config.js` and add your language's locale code to the `locales` array
(e.g. `locales: ["en"],` -> `locales: ["en", "fr"],`). Locale codes can be either just
the language (`en`) or include a region (`en-US`).
2. Open `lang/strings.js` and copy/paste the entire English strings block. Replace the
locale code in `AllStrings["en"]` with the code you added in step 1.
3. Translate all the newly copypasted strings.
4. Make a pull request.
15 changes: 10 additions & 5 deletions components/page_meta.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Head from "next/head"
import Script from "next/script"
import React from "react"
import React, { useContext } from "react"
import { LangContext } from "../lang/dict_manager"

function GATag(props) {
return <>
Expand All @@ -15,23 +16,27 @@ function GATag(props) {
}

export function CommonMetadata() {
const lang = useContext(LangContext)
return <>
<Head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta name="theme-color" content="#c3f0ce" />
<meta content="I MISS FAUNA" property="og:title" />
<meta content={lang.CommonMetadata.HeaderSMTitle} property="og:title" />
<meta name="twitter:card" content="summary_large_image" />
</Head>
{process.env.NEXT_PUBLIC_GA_TAG ? <GATag tag={process.env.NEXT_PUBLIC_GA_TAG} /> : null}
</>
}

export function CommonFooter(props) {
const lang = useContext(LangContext)
return <footer>
<a href={props.channelLink}>Ceres Fauna Ch. hololive-EN</a> <br />
<a href={props.channelLink}>{lang.CommonMetadata.FooterStreamerLink}</a> <br />
<small>
Not affiliated with Fauna or hololive - Past stream data
provided by Holodex - <a href="https://github.com/saplinganon/imissfauna.com">Source</a>
{lang.formatString(
lang.CommonMetadata.FooterText,
<a href="https://github.com/saplinganon/imissfauna.com">{lang.CommonMetadata.FooterSourceLink}</a>
)}
</small>
</footer>
}
13 changes: 3 additions & 10 deletions components/past_stream_counter.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import React from "react"
import useSWR from "swr"
import { useDictionary } from "../lang/dict_manager"
import styles from "../styles/Home.module.css"
import { TextCountdown } from "./text_countdown"

const COUNTDOWN_FORMATS = {
immediate: "", forFuture: "", forPast: `%@ without Fauna`,
days: (days) => (days > 1 ? `${days} days` : `${days} day`),
hours: (hours) => (hours > 1 ? `${hours} hours` : `${hours} hour`),
minutes: (minutes) => (minutes > 1 ? `${minutes} minutes` : `${minutes} minute`),
seconds: (seconds) => (seconds > 1 ? `${seconds} seconds` : `${seconds} second`),
separator: ", "
}

async function fetchPastStream(url) {
const response = await fetch(url)
const apiJSON = await response.json()
Expand All @@ -31,14 +23,15 @@ export function PastStreamCounter(props) {
revalidateIfStale: true,
refreshInterval: 90000,
})
const dict = useDictionary()

if (!data) {
return null
}

return <div className={`${styles.streamInfo} ${styles.pastStreamInfo}`}>
<a href={data?.link}>
<TextCountdown to={data?.date} formatStrings={COUNTDOWN_FORMATS} />
<TextCountdown to={data?.date} formatStrings={dict.Countdowns.PastStream} />
</a>
</div>
}
18 changes: 8 additions & 10 deletions components/video_box.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import React from "react"
import React, { useContext } from "react"
import { LangContext, useDictionary } from "../lang/dict_manager"
import styles from "../styles/Home.module.css"
import { TextCountdown } from "./text_countdown"

const COUNTDOWN_FORMATS = {
immediate: "(Now!)",
forFuture: "(in %@)",
forPast: "(%@ ago)",
}

export function VideoBox(props) {
const lang = useContext(LangContext)
const dict = useDictionary()

return <div className={`${styles.videoBox}`}>
<div className={styles.vstack}>
{props.caption?
<p className={`${styles.videoBoxCaption}`}>
{props.caption} {" "}
{(props.showCountdown && props.info.startTime)?
<span className={styles.countdown}><TextCountdown to={props.info.startTime} formatStrings={COUNTDOWN_FORMATS} /></span>
<span className={styles.countdown}><TextCountdown to={props.info.startTime} formatStrings={dict.Countdowns.VideoBox} /></span>
: null
}
</p>
: null
}
<p><a href={props.info.link}>{props.info.title}</a></p>
{props.info.isMembersOnly ? <p>(for Faunatics only!)</p> : null}
{props.info.isMembersOnly ? <p>{lang.VideoBox.MembersOnlySubtext}</p> : null}
</div>
{props.info.thumbnail ? <img src={props.info.thumbnail} alt="thumbnail" width={120} /> : null}
{props.info.thumbnail ? <img src={props.info.thumbnail} alt={lang.VideoBox.ThumbnailAltText} width={120} /> : null}
</div>
}
23 changes: 23 additions & 0 deletions lang/dict_manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useRouter } from "next/router"
import { createContext } from "react"
import LocalizedStrings from "react-localization"
import AllStrings from "./strings"

const dummyEmptyLocalizedStrings = new LocalizedStrings({"a": "aa"})
export const LangContext = createContext(dummyEmptyLocalizedStrings)

export function useLangCode() {
const { locale, defaultLocale } = useRouter()
const resolvedLocale = locale? locale : defaultLocale
return resolvedLocale
}

export function useDictionary() {
const langCode = useLangCode()
return AllStrings[langCode]
}

export function useLocalizationForRootComponentsOnly() {
const langCode = useLangCode()
return new LocalizedStrings(AllStrings, {customLanguageInterface: () => langCode})
}
76 changes: 76 additions & 0 deletions lang/strings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const AllStrings = {}

// To add a new language, copypaste starting from this line...
AllStrings["en"] = {
CommonMetadata: {
HeaderSMTitle: "I MISS FAUNA",
FooterText: "Not affiliated with Fauna or hololive - Past stream data provided by Holodex - {0}",
FooterSourceLink: "Source",
FooterStreamerLink: "Ceres Fauna Ch. hololive-EN"
},

Main: {
PageTitle: "I MISS FAUNA",
DontMissCaption: "I Don't Miss Fauna",
ImageAlt: "Meme",
RandomVodLink: "Do your reps",
ErrorMessageChannelLink: "You can check Fauna's channel yourself",
ErrorOccurred: "There was a problem checking stream status. {0}!",
Embed: {
TextLive: "Streaming: {0}",
TextStartingSoon: "Streaming: {0}",
TextStreamQueued: "Streaming: {0}",
}
},

VideoBox: {
StatusLive: "LIVE",
StatusStartingSoon: "Starting Soon",
StatusStreamQueued: "Next Stream",
NoStreamDummyStatus: "Current Stream",
NoStreamDummyTitle: "NOTHING UUUUUUUuuuuuu",
MembersOnlySubtext: "(for Faunatics only!)",
ThumbnailAltText: "Video Thumbnail"
},

Reps: {
PageTitle: "Do your reps!",
SMMetaDescription: "Get a random Fauna VOD to watch!",
VodInfoUploadDate: "Streamed or uploaded on {0}",
PageCaption: "Watch this one!",
RerollButton: "Reroll",
BackToStreamTrackerButton: "Back to stream tracker",
ErrorDescription: "A problem occurred while getting a random video: {0}",

ErrorCodes: {
NO_VIDEO_FOUND: "No video found."
},
},

Countdowns: {
VideoBox: {
immediate: "(Now!)",
forFuture: "(in %@)",
forPast: "(%@ ago)",
days: "%@d",
hours: "%@h",
minutes: "%@m",
seconds: "%@s",
separator: " ",
},
PastStream: {
immediate: "",
forFuture: "",
forPast: `%@ without Fauna`,
days: (days) => (days > 1 ? `${days} days` : `${days} day`),
hours: (hours) => (hours > 1 ? `${hours} hours` : `${hours} hour`),
minutes: (minutes) => (minutes > 1 ? `${minutes} minutes` : `${minutes} minute`),
seconds: (seconds) => (seconds > 1 ? `${seconds} seconds` : `${seconds} second`),
separator: ", "
}
},
FormatDateShort: (date) => date.toLocaleDateString("en-US", {month: "short", day: "numeric", year: "numeric"})
}
// ...down to this line.

export default AllStrings
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module.exports = {
reactStrictMode: true,
i18n: {
locales: ["en"],
defaultLocale: "en"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"pg": "^8.7.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-localization": "^1.0.19",
"sqlite3": "^5.0.2",
"swr": "^1.3.0"
},
Expand Down
2 changes: 1 addition & 1 deletion pages/api/v2/random_vod.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default async function handler(req, res) {
})
} else {
res.status(503).json({
error: "No video found.",
error: "NO_VIDEO_FOUND",
})
}

Expand Down
Loading

0 comments on commit 85edd86

Please sign in to comment.