support translations of the games

This commit is contained in:
Jon Eugster
2024-03-27 00:22:51 +01:00
parent a26022e3fc
commit ad1add5264
12 changed files with 61 additions and 18 deletions
+9 -5
View File
@@ -19,6 +19,7 @@ import { PreferencesPopup } from './popup/preferences';
import { ImpressumButton, MenuButton, PreferencesButton } from './app_bar';
import ReactCountryFlag from 'react-country-flag';
import lean4gameConfig from '../config.json'
import i18next from 'i18next';
function GithubIcon({url='https://github.com'}) {
@@ -32,7 +33,7 @@ function GithubIcon({url='https://github.com'}) {
}
function Tile({gameId, data}: {gameId: string, data: GameTile|undefined}) {
let { t } = useTranslation()
let { t, i18n } = useTranslation()
let navigate = useNavigate();
const routeChange = () =>{
navigate(gameId);
@@ -42,19 +43,22 @@ function Tile({gameId, data}: {gameId: string, data: GameTile|undefined}) {
return <></>
}
// Load the namespace of the game
i18next.loadNamespaces(gameId)
return <div className="game" onClick={routeChange}>
<div className="wrapper">
<div className="title">{data.title}</div>
<div className="short-description">{data.short}
<div className="title">{t(data.title, { ns: gameId })}</div>
<div className="short-description">{t(data.short, { ns: gameId })}
</div>
{ data.image ? <img className="image" src={path.join("data", gameId, data.image)} alt="" /> : <div className="image"/> }
<div className="long description"><Markdown>{data.long}</Markdown></div>
<div className="long description"><Markdown>{t(data.long, { ns: gameId })}</Markdown></div>
</div>
<table className="info">
<tbody>
<tr>
<td title="consider playing these games first.">{t("Prerequisites")}</td>
<td><Markdown>{data.prerequisites.join(', ')}</Markdown></td>
<td><Markdown>{t(data.prerequisites.join(', '), { ns: gameId })}</Markdown></td>
</tr>
<tr>
<td>{t("Worlds")}</td>
+10 -2
View File
@@ -53,6 +53,8 @@ import { onigasmH } from 'onigasm/lib/onigasmH'
import { isLastStepWithErrors, lastStepHasErrors } from './infoview/goals'
import { InfoPopup } from './popup/game_info'
import { PreferencesPopup } from './popup/preferences'
import { useTranslation } from 'react-i18next'
import i18next from 'i18next'
monacoSetup()
@@ -63,6 +65,10 @@ function Level() {
const worldId = params.worldId
const gameId = React.useContext(GameIdContext)
// Load the namespace of the game
i18next.loadNamespaces(gameId)
const gameInfo = useGetGameInfoQuery({game: gameId})
// pop-ups
@@ -88,6 +94,7 @@ function Level() {
}
function ChatPanel({lastLevel, visible = true}) {
let { t } = useTranslation()
const chatRef = useRef<HTMLDivElement>(null)
const {mobile} = useContext(PreferencesContext)
const gameId = useContext(GameIdContext)
@@ -134,7 +141,7 @@ function ChatPanel({lastLevel, visible = true}) {
// // chatRef.current!.scrollTo(0,0)
// }, [gameId, worldId, levelId])
let introText: Array<string> = level?.data?.introduction.split(/\n(\s*\n)+/)
let introText: Array<string> = t(level?.data?.introduction, {ns: gameId}).split(/\n(\s*\n)+/)
return <div className={`chat-panel ${visible ? '' : 'hidden'}`}>
<div ref={chatRef} className="chat">
@@ -441,11 +448,12 @@ function PlayableLevel({impressum, setImpressum, toggleInfo, togglePreferencesPo
}
function IntroductionPanel({gameInfo}) {
let { t } = useTranslation()
const gameId = React.useContext(GameIdContext)
const {worldId} = useContext(WorldLevelIdContext)
const {mobile} = React.useContext(PreferencesContext)
let text: Array<string> = gameInfo.data?.worlds.nodes[worldId].introduction.split(/\n(\s*\n)+/)
let text: Array<string> = t(gameInfo.data?.worlds.nodes[worldId].introduction, {ns: gameId}).split(/\n(\s*\n)+/)
return <div className="chat-panel">
<div className="chat">
+10 -1
View File
@@ -23,19 +23,24 @@ import { WorldTreePanel } from './world_tree'
import '../css/welcome.css'
import { WelcomeAppBar } from './app_bar'
import { Hint } from './hints'
import i18next from 'i18next'
import { useTranslation } from 'react-i18next'
/** the panel showing the game's introduction text */
function IntroductionPanel({introduction, setPageNumber}: {introduction: string, setPageNumber}) {
const {mobile} = React.useContext(PreferencesContext)
const gameId = React.useContext(GameIdContext)
let { t } = useTranslation()
const dispatch = useAppDispatch()
// TODO: I left the setup for splitting up the introduction in place, but if it's not needed
// then this can be simplified.
// let text: Array<string> = introduction.split(/\n(\s*\n)+/)
let text: Array<string> = introduction ? [introduction] : []
let text: Array<string> = introduction ? [t(introduction, {ns : gameId})] : []
return <div className="column chat-panel">
<div className="chat">
@@ -64,6 +69,10 @@ function IntroductionPanel({introduction, setPageNumber}: {introduction: string,
/** main page of the game showing among others the tree of worlds/levels */
function Welcome() {
const gameId = React.useContext(GameIdContext)
// Load the namespace of the game
i18next.loadNamespaces(gameId)
const {mobile} = React.useContext(PreferencesContext)
const {layout, isSavePreferences, language, setLayout, setIsSavePreferences, setLanguage} = React.useContext(PreferencesContext)
+13
View File
@@ -6,6 +6,19 @@ i18n
.use(initReactI18next) // passes i18n down to react-i18next
.use(Backend)
.init({
ns: ['translation'],
backend: {
// see https://github.com/i18next/i18next-http-backend
loadPath: function(lngs, namespaces: Array<string>) {
console.warn(namespaces)
if (namespaces[0].startsWith("g/")) {
return '/i18n/{{ns}}/{{lng}}/Game.json';
} else {
return '/locales/{{lng}}/{{ns}}.json';
}
}
},
lng: "en", // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option