support translations of the games

pull/205/head
Jon Eugster 2 years ago
parent a26022e3fc
commit ad1add5264

@ -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>

@ -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">

@ -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)

@ -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

@ -37,6 +37,14 @@ router.get('/import/trigger/:owner/:repo', importTrigger)
const server = app
.use(express.static(path.join(__dirname, '..', 'client', 'dist'))) // TODO: add a dist folder from inside the game
.use('/i18n/g/:owner/:repo/:lang/*', (req, res, next) => {
const owner = req.params.owner;
const repo = req.params.repo
const lang = req.params.lang
const filename = req.params[0];
req.url = filename;
express.static(path.join(getGameDir(owner,repo),".i18n",lang))(req, res, next);
})
.use('/data/g/:owner/:repo/*', (req, res, next) => {
const owner = req.params.owner;
const repo = req.params.repo

@ -331,6 +331,9 @@ structure GameParams where
diagnostics : GameDiagnostics
deriving ToJson, FromJson
-- `snap` and `initParams` are unused
set_option linter.unusedVariables false in
/-- WIP: publish diagnostics, all intermediate goals and if the game is completed. -/
def publishProofState (m : DocumentMeta) (snap : Snapshot) (initParams : Lsp.InitializeParams) (hOut : FS.Stream) :
IO Unit := do

@ -14,9 +14,6 @@ open Lean.Parser Term
open PrettyPrinter Delaborator SubExpr
open TSyntax.Compat
#check Command.declSig
open private shouldGroupWithNext evalSyntaxConstant from Lean.PrettyPrinter.Delaborator.Builtins
-- def typeSpec := leading_parser " :\\n: " >> termParser

@ -59,8 +59,8 @@ def saveGameData (allItemsByType : HashMap InventoryType (HashSet Name))
IO.FS.writeFile (path / inventoryFileName) (toString (toJson inventory))
-- write PO file for translation
I18n.createPOTemplate
-- write file for translation
I18n.createTemplate
open GameData

@ -52,8 +52,6 @@ If names are provided, it will introduce as many `let` statements as there are n
syntax (name := letIntros) "let_intros" : tactic
-- (ppSpace colGt (ident <|> hole))*
#check letIntros
@[tactic letIntros] def evalLetIntros : Tactic := fun stx => do
match stx with
| `(tactic| let_intros) => liftMetaTactic fun mvarId => do

@ -13,10 +13,10 @@
{"url": "https://github.com/hhu-adam/lean-i18n.git",
"type": "git",
"subDir": null,
"rev": "c5b84feffb28dbd5b1ac74b3bf63271296fabfa5",
"rev": "59fea5bbc2ea22c2ee77ea6a65b0212ac241a56f",
"name": "i18n",
"manifestFile": "lake-manifest.json",
"inputRev": "v4.6.0",
"inputRev": "main",
"inherited": false,
"configFile": "lakefile.lean"}],
"name": "GameServer",

@ -8,7 +8,7 @@ package GameServer
def leanVersion := "v4.6.0" -- TODO
require std from git "https://github.com/leanprover/std4.git" @ leanVersion
require i18n from git "https://github.com/hhu-adam/lean-i18n.git" @ leanVersion
require i18n from git "https://github.com/hhu-adam/lean-i18n.git" @ "main" -- leanVersion
lean_lib GameServer

@ -44,6 +44,9 @@ export default defineConfig({
'/data': {
target: 'http://localhost:8080',
},
'/i18n': {
target: 'http://localhost:8080',
},
}
},
resolve: {

Loading…
Cancel
Save