(refactor) add i18n to frontend and refactor

This commit is contained in:
Halit Aksoy 2024-06-17 23:50:13 +03:00
parent 02277287d0
commit 66b61215c1
21 changed files with 196 additions and 258 deletions

View File

@ -1,4 +1,5 @@
export * from './models/index' export * from './models/index'
export * from './rtmt/index' export * from './rtmt/index'
export * from './theme/index' export * from './theme/index'
export * from './storage/storage' export * from './storage/storage'
export * from './localization/index'

View File

@ -0,0 +1,40 @@
export const en = {
Mancala: "Mancala",
Leave: "Leave The Game",
NewGame: "New Game",
YourTurn: "Your Turn",
OpponentTurn: "Opponent Turn",
GameEnded: "Game Ended",
InternalErrorOccurred: "An internal error has occurred",
YouWon: "You Won",
Won: "Won",
YouLost: "You Lost",
Connecting: "Connecting",
Connected: "Connected",
CannotConnect: "Can't Connect",
ConnectionLost: "Network Connection Lost",
ConnectingAgain: "Connecting Again",
ServerError: "Server Error",
SearchingOpponet: "Searching Opponet",
OpponentLeftTheGame: "Opponent Leaves The Game",
YouLeftTheGame: "You Left The Game",
UserLeftTheGame: "Left The Game",
SearchingOpponent: "Searching Opponent",
PleaseWait: "Please Wait",
GameDraw: "Draw",
Anonymous: "Anonymous",
GameNotFound: "Game Not Found",
Loading: "Loading",
Playing: "Playing",
Error: "Error",
ErrorWhenRetrievingInformation: "An error occured when retrieving information!",
UCanOnlyPlayYourOwnPits: "You can only play your own pits",
UMustWaitUntilCurrentMoveComplete: "You must wait until the current move is complete",
UCanNotPlayEmptyPit: "You can not play empty pit",
AreYouSureToLeaveGame: "Are you sure to leave game?",
Yes: "Yes",
Cancel: "Cancel",
Help: "Help",
WebApp: "Goto WebApp",
Privacy: "Open Privacy Policy"
}

View File

@ -0,0 +1,2 @@
export * from './en'
export * from './tr'

View File

@ -0,0 +1,40 @@
export const tr = {
Mancala: "Köçürme",
Leave: "Oyundan Ayrıl",
NewGame: "Yeni Oyun",
YourTurn: "Sıra Sende",
OpponentTurn: "Sıra Rakipte",
GameEnded: "Oyun Bitti",
InternalErrorOccurred: "Dahili bir hata oluştu",
YouWon: "Kazandın",
Won: "Kazandı",
YouLost: "Kaybettin",
Connecting: "Bağlanılıyor",
Connected: "Bağlandı",
CannotConnect: "Bağlanılamadı",
ConnectionLost: "Ağ Bağlantısı Koptu",
ConnectingAgain: "Tekrar Bağlanılıyor",
ServerError: "Sunucu Hatası",
SearchingOpponet: "Rakip Aranıyor",
OpponentLeftTheGame: "Rakip Oyundan Ayrıldı",
YouLeftTheGame: "Sen Oyundan Ayrıldın",
UserLeftTheGame: "Oyundan Ayrıldı",
SearchingOpponent: "Rakip Aranıyor",
PleaseWait: "Lütfen Bekleyin",
GameDraw: "Berabere",
Anonymous: "Anonim",
GameNotFound: "Oyun Bulunamadı",
Loading: "Yükleniyor",
Playing: "Oynuyor",
Error: "Hata",
ErrorWhenRetrievingInformation: "Bilgiler toplanırken bir hata oluştu!",
UCanOnlyPlayYourOwnPits: "Sadece sana ait olan kuyular ile oynayabilirsin",
UMustWaitUntilCurrentMoveComplete: "Devam eden haraketin bitmesini beklemelisin",
UCanNotPlayEmptyPit: "Boş kuyu ile oynayamazsın",
AreYouSureToLeaveGame: "Oyundan ayrılmak istediğine emin misin?",
Yes: "Evet",
Cancel: "İptal",
Help: "Yardım",
WebApp: "Web Uygulamasına Git",
Privacy: "Gizlilik Politikasını Göster"
};

View File

@ -2,5 +2,6 @@ import * as React from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
const container = document.getElementById('main'); const container = document.getElementById('main');
const root = createRoot(container!); const root = createRoot(container!);
import './localization/i18n';
import MancalaApp from './MancalaApp'; import MancalaApp from './MancalaApp';
root.render(<MancalaApp/>); root.render(<MancalaApp/>);

View File

@ -12,17 +12,20 @@ import Home from './routes/Home';
import { initContext } from './context/context'; import { initContext } from './context/context';
import { RTMTWS, ConnectionState } from '@mancala/core'; import { RTMTWS, ConnectionState } from '@mancala/core';
import { getColorByBrightness } from './util/ColorUtil'; import { getColorByBrightness } from './util/ColorUtil';
import { Theme } from './theme/Theme'; import { Theme } from '@mancala/core';
import LobyPage from './routes/LobyPage'; import LobyPage from './routes/LobyPage';
import PrivacyPage from './routes/PrivacyPage'; import PrivacyPage from './routes/PrivacyPage';
import swal from 'sweetalert'; import swal from 'sweetalert';
import Util from './util/Util'; import Util from './util/Util';
import { server } from './const/config'; import { server } from './const/config';
import { useTranslation } from 'react-i18next';
const context = initContext(); const context = initContext();
const MancalaApp: FunctionComponent = () => { const MancalaApp: FunctionComponent = () => {
const { t } = useTranslation();
const [userKey, setUserKey] = useState<string | undefined>(undefined); const [userKey, setUserKey] = useState<string | undefined>(undefined);
const [connectionState, setConnetionState] = useState<ConnectionState>("connecting"); const [connectionState, setConnetionState] = useState<ConnectionState>("connecting");
@ -50,7 +53,7 @@ const MancalaApp: FunctionComponent = () => {
connectRTMT(userKey); connectRTMT(userKey);
}).catch((error) => { }).catch((error) => {
//TODO: check if it is network error! //TODO: check if it is network error!
swal(context.texts.Error + "!", context.texts.ErrorWhenRetrievingInformation, "error"); swal(t("Error") + "!", t("ErrorWhenRetrievingInformation"), "error");
console.error(error); console.error(error);
}); });
} }
@ -88,7 +91,7 @@ const MancalaApp: FunctionComponent = () => {
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
<FloatingPanel context={context} color={context.themeManager.theme.boardColor} visible={connectionState != "connected"}> <FloatingPanel context={context} color={context.themeManager.theme.boardColor} visible={connectionState != "connected"}>
<span style={{ color: textColorOnBoard, transition: 'color 0.5s' }}>{Util.getTextByConnectionState(context, connectionState)}</span> <span style={{ color: textColorOnBoard, transition: 'color 0.5s' }}>{Util.getTextByConnectionState(context, connectionState, t)}</span>
</FloatingPanel> </FloatingPanel>
</> </>
); );

View File

@ -4,9 +4,11 @@ import { Context } from "../context/context";
import { Game, User } from "@mancala/core"; import { Game, User } from "@mancala/core";
import { getColorByBrightness } from "../util/ColorUtil"; import { getColorByBrightness } from "../util/ColorUtil";
import CircularPanel from "./CircularPanel"; import CircularPanel from "./CircularPanel";
import { useTranslation } from "react-i18next";
function getInfoPanelTextByGameState(params: { function getInfoPanelTextByGameState(params: {
context: Context; context: Context;
t: (text: string) => string;
game?: Game; game?: Game;
currentUser: User; currentUser: User;
whitePlayer: User; whitePlayer: User;
@ -16,6 +18,7 @@ function getInfoPanelTextByGameState(params: {
}): string | undefined { }): string | undefined {
const { const {
context, context,
t,
game, game,
currentUser, currentUser,
whitePlayer, whitePlayer,
@ -25,8 +28,8 @@ function getInfoPanelTextByGameState(params: {
} = params; } = params;
if (leftPlayer) { if (leftPlayer) {
return isSpectator ? `${leftPlayer.name} ${context.texts.UserLeftTheGame}` : return isSpectator ? `${leftPlayer.name} ${t("UserLeftTheGame")}` :
leftPlayer.id == currentUser.id ? context.texts.YouLeftTheGame : context.texts.OpponentLeftTheGame; leftPlayer.id == currentUser.id ? t("YouLeftTheGame") : t("OpponentLeftTheGame");
} }
const isGameEnded = game?.mancalaGame.state == "ended"; const isGameEnded = game?.mancalaGame.state == "ended";
@ -35,21 +38,21 @@ function getInfoPanelTextByGameState(params: {
let whoWon; let whoWon;
if (wonPlayerID) { if (wonPlayerID) {
const wonPlayer = wonPlayerID == whitePlayer.id ? whitePlayer : blackPlayer; const wonPlayer = wonPlayerID == whitePlayer.id ? whitePlayer : blackPlayer;
whoWon = isSpectator ? `${wonPlayer.name} ${context.texts.Won}` : whoWon = isSpectator ? `${wonPlayer.name} ${t("Won")}` :
game.mancalaGame.getWonPlayerId() === currentUser.id game.mancalaGame.getWonPlayerId() === currentUser.id
? context.texts.YouWon ? t("YouWon")
: context.texts.YouLost; : t("YouLost");
} else { } else {
whoWon = context.texts.GameDraw; whoWon = t("GameDraw");
} }
return context.texts.GameEnded + " " + whoWon; return t("GameEnded") + " " + whoWon;
} }
if (game) { if (game) {
const playingPlayer = game.mancalaGame.checkIsPlayerTurn(whitePlayer.id) ? whitePlayer : blackPlayer; const playingPlayer = game.mancalaGame.checkIsPlayerTurn(whitePlayer.id) ? whitePlayer : blackPlayer;
return isSpectator ? `${playingPlayer.name} ${context.texts.Playing}` : game.mancalaGame.checkIsPlayerTurn(currentUser.id) return isSpectator ? `${playingPlayer.name} ${t("Playing")}` : game.mancalaGame.checkIsPlayerTurn(currentUser.id)
? context.texts.YourTurn ? t("YourTurn")
: context.texts.OpponentTurn; : t("OpponentTurn");
} }
return undefined; return undefined;
@ -76,6 +79,9 @@ const InfoPanel: FunctionComponent<{
visible, visible,
isSpectator isSpectator
}) => { }) => {
const { t } = useTranslation();
if (visible === false) return <></>; if (visible === false) return <></>;
const isUserTurn = currentUser.id ? game?.mancalaGame.checkIsPlayerTurn(currentUser.id) : false; const isUserTurn = currentUser.id ? game?.mancalaGame.checkIsPlayerTurn(currentUser.id) : false;
const containerColor = isUserTurn const containerColor = isUserTurn
@ -88,6 +94,7 @@ const InfoPanel: FunctionComponent<{
); );
const text = getInfoPanelTextByGameState({ const text = getInfoPanelTextByGameState({
context, context,
t,
game, game,
currentUser, currentUser,
whitePlayer, whitePlayer,

View File

@ -4,8 +4,12 @@ import { Context } from '../context/context';
import { LoadingState } from "@mancala/core"; import { LoadingState } from "@mancala/core";
import { getColorByBrightness } from '../util/ColorUtil'; import { getColorByBrightness } from '../util/ColorUtil';
import CircularPanel from './CircularPanel'; import CircularPanel from './CircularPanel';
import { useTranslation } from 'react-i18next';
const LoadingComponent: FunctionComponent<{ context: Context, loadingState: LoadingState<any> }> = ({ context, loadingState }) => { const LoadingComponent: FunctionComponent<{ context: Context, loadingState: LoadingState<any> }> = ({ context, loadingState }) => {
const { t } = useTranslation();
if (loadingState.isUnset() || loadingState.isLoaded()) { if (loadingState.isUnset() || loadingState.isLoaded()) {
return <></> return <></>
} }
@ -15,7 +19,7 @@ const LoadingComponent: FunctionComponent<{ context: Context, loadingState: Load
context.themeManager.theme.textColor, context.themeManager.theme.textColor,
context.themeManager.theme.textLightColor context.themeManager.theme.textLightColor
); );
const text = loadingState.isLoading() ? context.texts.Loading +"..." : loadingState.errorMessage; const text = loadingState.isLoading() ? t("Loading") +"..." : loadingState.errorMessage;
return ( return (
<CircularPanel color={context.themeManager.theme.boardColor}> <CircularPanel color={context.themeManager.theme.boardColor}>
<h4 style={{ margin: "0", color: textColorOnBoard }}>{`${text}`}</h4> <h4 style={{ margin: "0", color: textColorOnBoard }}>{`${text}`}</h4>

View File

@ -4,6 +4,7 @@ import { Context } from '../context/context';
import { User } from "@mancala/core"; import { User } from "@mancala/core";
import { getColorByBrightness } from '../util/ColorUtil'; import { getColorByBrightness } from '../util/ColorUtil';
import Space from './Space'; import Space from './Space';
import { useTranslation } from 'react-i18next';
export type LayoutMode = "right" | "left"; export type LayoutMode = "right" | "left";
@ -15,6 +16,9 @@ const UserStatus: FunctionComponent<{
style?: React.CSSProperties style?: React.CSSProperties
}> = ({ context, user, layoutMode, visible, style }) => { }> = ({ context, user, layoutMode, visible, style }) => {
if (visible === false) return <></>; if (visible === false) return <></>;
const { t } = useTranslation();
const textColorOnBoard = getColorByBrightness( const textColorOnBoard = getColorByBrightness(
context.themeManager.theme.background, context.themeManager.theme.background,
context.themeManager.theme.textColor, context.themeManager.theme.textColor,
@ -22,7 +26,7 @@ const UserStatus: FunctionComponent<{
); );
return ( return (
<div style={style} className={layoutMode === "right" ? "flex-rtl" : "flex-ltr"}> <div style={style} className={layoutMode === "right" ? "flex-rtl" : "flex-ltr"}>
<span style={{color: textColorOnBoard, transition: 'color 0.5s'}} className='text'>{user.isAnonymous ? context.texts.Anonymous : user.name}</span> <span style={{color: textColorOnBoard, transition: 'color 0.5s'}} className='text'>{user.isAnonymous ? t("Anonymous") : user.name}</span>
<Space width='5px' /> <Space width='5px' />
<div className={"circle " + (user.isOnline ? "online" : "offline")}></div> <div className={"circle " + (user.isOnline ? "online" : "offline")}></div>
<style jsx>{` <style jsx>{`

View File

@ -1,6 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import { FunctionComponent } from 'react'; import { FunctionComponent } from 'react';
import { Context } from '../../context/context';
const HeaderbarTitle: FunctionComponent<{ title: string, color: string }> = ({ title, color }) => { const HeaderbarTitle: FunctionComponent<{ title: string, color: string }> = ({ title, color }) => {
return ( return (

View File

@ -1,114 +0,0 @@
export type Texts = {
Mancala: string,
Leave: string,
NewGame: string,
YourTurn: string,
OpponentTurn: string,
GameEnded: string,
InternalErrorOccurred: string,
YouWon: string,
Won: string,
YouLost: string,
Connecting: string,
Connected: string,
CannotConnect: string,
ConnectionLost: string,
ConnectingAgain: string,
ServerError: string,
SearchingOpponet: string,
OpponentLeftTheGame: string,
YouLeftTheGame: string,
UserLeftTheGame: string,
SearchingOpponent: string,
PleaseWait: string,
GameDraw: string,
Anonymous: string,
GameNotFound: string,
Loading: string,
Playing: string,
Error: string,
ErrorWhenRetrievingInformation: string,
UCanOnlyPlayYourOwnPits: string,
UMustWaitUntilCurrentMoveComplete: string,
UCanNotPlayEmptyPit: string,
AreYouSureToLeaveGame: string,
Yes: string,
Cancel: string,
}
export const EnUs: Texts = {
Mancala: "Mancala",
Leave: "Leave The Game",
NewGame: "New Game",
YourTurn: "Your Turn",
OpponentTurn: "Opponent Turn",
GameEnded: "Game Ended",
InternalErrorOccurred: "An internal error has occurred",
YouWon: "You Won",
Won: "Won",
YouLost: "You Lost",
Connecting: "Connecting",
Connected: "Connected",
CannotConnect: "Can't Connect",
ConnectionLost: "Network Connection Lost",
ConnectingAgain: "Connecting Again",
ServerError: "Server Error",
SearchingOpponet: "Searching Opponet",
OpponentLeftTheGame: "Opponent Leaves The Game",
YouLeftTheGame: "You Left The Game",
UserLeftTheGame: "Left The Game",
SearchingOpponent: "Searching Opponent",
PleaseWait: "Please Wait",
GameDraw: "Draw",
Anonymous: "Anonymous",
GameNotFound: "Game Not Found",
Loading: "Loading",
Playing: "Playing",
Error: "Error",
ErrorWhenRetrievingInformation: "An error occured when retrieving information!",
UCanOnlyPlayYourOwnPits: "You can only play your own pits",
UMustWaitUntilCurrentMoveComplete: "You must wait until the current move is complete",
UCanNotPlayEmptyPit: "You can not play empty pit",
AreYouSureToLeaveGame: "Are you sure to leave game?",
Yes: "Yes",
Cancel: "Cancel",
}
export const TrTr: Texts = {
Mancala: "Köçürme",
Leave: "Oyundan Ayrıl",
NewGame: "Yeni Oyun",
YourTurn: "Sıra Sende",
OpponentTurn: "Sıra Rakipte",
GameEnded: "Oyun Bitti",
InternalErrorOccurred: "Dahili bir hata oluştu",
YouWon: "Kazandın",
Won: "Kazandı",
YouLost: "Kaybettin",
Connecting: "Bağlanılıyor",
Connected: "Bağlandı",
CannotConnect: "Bağlanılamadı",
ConnectionLost: "Ağ Bağlantısı Koptu",
ConnectingAgain: "Tekrar Bağlanılıyor",
ServerError: "Sunucu Hatası",
SearchingOpponet: "Rakip Aranıyor",
OpponentLeftTheGame: "Rakip Oyundan Ayrıldı",
YouLeftTheGame: "Sen Oyundan Ayrıldın",
UserLeftTheGame: "Oyundan Ayrıldı",
SearchingOpponent: "Rakip Aranıyor",
PleaseWait: "Lütfen Bekleyin",
GameDraw: "Berabere",
Anonymous: "Anonim",
GameNotFound: "Oyun Bulunamadı",
Loading: "Yükleniyor",
Playing: "Oynuyor",
Error: "Hata",
ErrorWhenRetrievingInformation: "Bilgiler toplanırken bir hata oluştu!",
UCanOnlyPlayYourOwnPits: "Sadece sana ait olan kuyular ile oynayabilirsin",
UMustWaitUntilCurrentMoveComplete: "Devam eden haraketin bitmesini beklemelisin",
UCanNotPlayEmptyPit: "Boş kuyu ile oynayamazsın",
AreYouSureToLeaveGame: "Oyundan ayrılmak istediğine emin misin?",
Yes: "Evet",
Cancel: "İptal"
}

View File

@ -1,5 +1,4 @@
import { server } from "../const/config"; import { server } from "../const/config";
import { Texts, TrTr } from "../const/texts";
import { RTMT, RTMTWS, ThemeManager } from "@mancala/core"; import { RTMT, RTMTWS, ThemeManager } from "@mancala/core";
import { HttpServiceImpl } from "../service/HttpService"; import { HttpServiceImpl } from "../service/HttpService";
import { GameStore, GameStoreImpl } from "../store/GameStore"; import { GameStore, GameStoreImpl } from "../store/GameStore";
@ -10,7 +9,6 @@ import { Storage } from '@mancala/core'
export type Context = { export type Context = {
rtmt: RTMT; rtmt: RTMT;
userKeyStore: UserKeyStore; userKeyStore: UserKeyStore;
texts: Texts;
themeManager: ThemeManager; themeManager: ThemeManager;
gameStore: GameStore; gameStore: GameStore;
storage: Storage; storage: Storage;
@ -21,13 +19,11 @@ export const initContext = (): Context => {
const httpService = new HttpServiceImpl(server.serverAdress); const httpService = new HttpServiceImpl(server.serverAdress);
const userKeyStore = new UserKeyStoreImpl({ httpService }); const userKeyStore = new UserKeyStoreImpl({ httpService });
const gameStore = new GameStoreImpl({ httpService }); const gameStore = new GameStoreImpl({ httpService });
const texts = TrTr;
const storage = new LocalStorage(); const storage = new LocalStorage();
const themeManager = new ThemeManager(storage); const themeManager = new ThemeManager(storage);
return { return {
rtmt: rtmt, rtmt: rtmt,
userKeyStore: userKeyStore, userKeyStore: userKeyStore,
texts: texts,
themeManager, themeManager,
gameStore, gameStore,
storage storage

View File

@ -0,0 +1,24 @@
// https://medium.com/@yashpalraj/implement-i18n-in-react-native-application-a4c573e2e2a6
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { en, tr } from '@mancala/core';
const resources = { // list of languages
en: {
translation: en
},
tr: {
translation: tr
}
};
i18n.use(initReactI18next) // passes i18n down to react-i18next
.init({
compatibilityJSON: 'v3', //To make it work for Android devices, add this line.
resources,
lng: 'en', // default language to use.
// if you're using a language detector, do not define the lng option
interpolation: {
escapeValue: false,
},
});
export default i18n;

View File

@ -21,13 +21,14 @@ import { channel_on_game_update, channel_on_game_crashed, channel_on_game_user_l
import { Context } from '../context/context'; import { Context } from '../context/context';
import useWindowDimensions from '../hooks/useWindowDimensions'; import useWindowDimensions from '../hooks/useWindowDimensions';
import { GameMove, LoadingState, Game, GameUsersConnectionInfo } from "@mancala/core"; import { GameMove, LoadingState, Game, GameUsersConnectionInfo } from "@mancala/core";
import { Theme } from '../theme/Theme'; import { Theme } from '@mancala/core';
import { getColorByBrightness } from '../util/ColorUtil'; import { getColorByBrightness } from '../util/ColorUtil';
import BoardViewModel from '../viewmodel/BoardViewModel'; import BoardViewModel from '../viewmodel/BoardViewModel';
import Center from '../components/Center'; import Center from '../components/Center';
import notyf from '../util/Notyf'; import notyf from '../util/Notyf';
import swal from 'sweetalert'; import swal from 'sweetalert';
import Util from '../util/Util'; import Util from '../util/Util';
import { useTranslation } from 'react-i18next';
const GamePage: FunctionComponent<{ const GamePage: FunctionComponent<{
context: Context, context: Context,
@ -56,6 +57,8 @@ const GamePage: FunctionComponent<{
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useTranslation();
const [gameLoadingState, setLoadingStateGame] = useState<LoadingState<Game>>(LoadingState.Unset()); const [gameLoadingState, setLoadingStateGame] = useState<LoadingState<Game>>(LoadingState.Unset());
@ -88,7 +91,7 @@ const GamePage: FunctionComponent<{
} }
const onGameCrashed = (message: any) => { const onGameCrashed = (message: any) => {
const newCrashMessage = message as string; const newCrashMessage = message as string;
notyf.error(context.texts.InternalErrorOccurred); notyf.error(t("InternalErrorOccurred"));
console.error("on_game_crash"); console.error("on_game_crash");
console.error(newCrashMessage); console.error(newCrashMessage);
} }
@ -136,11 +139,11 @@ const GamePage: FunctionComponent<{
const checkHasAnOngoingAction = () => hasOngoingAction; const checkHasAnOngoingAction = () => hasOngoingAction;
const onLeaveGameClick = () => { const onLeaveGameClick = () => {
if (Util.checkConnectionAndMaybeAlert(context)) return; if (Util.checkConnectionAndMaybeAlert(context, t)) return;
swal({ swal({
title: context.texts.AreYouSureToLeaveGame, title: t("AreYouSureToLeaveGame"),
icon: "warning", icon: "warning",
buttons: [context.texts.Yes, context.texts.Cancel], buttons: [t("Yes"), t("Cancel")],
dangerMode: true, dangerMode: true,
}) })
.then((cancel) => { .then((cancel) => {
@ -151,7 +154,7 @@ const GamePage: FunctionComponent<{
}; };
const onNewGameClick = () => { const onNewGameClick = () => {
if (Util.checkConnectionAndMaybeAlert(context)) return; if (Util.checkConnectionAndMaybeAlert(context, t)) return;
navigate("/loby") navigate("/loby")
}; };
@ -160,31 +163,31 @@ const GamePage: FunctionComponent<{
return; return;
} }
if(userKeyWhoLeave) { if(userKeyWhoLeave) {
notyf.error(context.texts.GameEnded); notyf.error(t("GameEnded"));
return; return;
} }
if(game.mancalaGame.state === "ended") { if(game.mancalaGame.state === "ended") {
notyf.error(context.texts.GameEnded); notyf.error(t("GameEnded"));
return; return;
} }
if (Util.checkConnectionAndMaybeAlert(context)) return; if (Util.checkConnectionAndMaybeAlert(context, t)) return;
if (game.mancalaGame.getPlayerIdByIndex(index) !== userKey) { if (game.mancalaGame.getPlayerIdByIndex(index) !== userKey) {
notyf.error(context.texts.UCanOnlyPlayYourOwnPits); notyf.error(t("UCanOnlyPlayYourOwnPits"));
return; return;
} }
const pitIndexForUser = index % (game.mancalaGame.board.totalPitCount() / 2); const pitIndexForUser = index % (game.mancalaGame.board.totalPitCount() / 2);
if (!game.mancalaGame.canPlayerMove(userKey, pitIndexForUser)) { if (!game.mancalaGame.canPlayerMove(userKey, pitIndexForUser)) {
notyf.error(context.texts.OpponentTurn); notyf.error(t("OpponentTurn"));
return; return;
} }
if (checkHasAnOngoingAction()) { if (checkHasAnOngoingAction()) {
notyf.error(context.texts.UMustWaitUntilCurrentMoveComplete); notyf.error(t("UMustWaitUntilCurrentMoveComplete"));
return; return;
} }
if (!boardViewModel) return; if (!boardViewModel) return;
//TODO: this check should be in mancala.js //TODO: this check should be in mancala.js
if (pit.stoneCount === 0) { if (pit.stoneCount === 0) {
notyf.error(context.texts.UCanNotPlayEmptyPit); notyf.error(t("UCanNotPlayEmptyPit"));
return; return;
} }
setHasOngoingAction(true); setHasOngoingAction(true);
@ -207,7 +210,7 @@ const GamePage: FunctionComponent<{
unlistenMessages = listenMessages(game, pitAnimator); unlistenMessages = listenMessages(game, pitAnimator);
setLoadingStateGame(LoadingState.Loaded({ value: game })) setLoadingStateGame(LoadingState.Loaded({ value: game }))
} else { } else {
setLoadingStateGame(LoadingState.Error({ errorMessage: context.texts.GameNotFound })) setLoadingStateGame(LoadingState.Error({ errorMessage: t("GameNotFound") }))
} }
}) })
return () => { return () => {
@ -273,7 +276,7 @@ const GamePage: FunctionComponent<{
<HeaderbarIcon /> <HeaderbarIcon />
</Link> </Link>
<Link style={{ textDecoration: 'none' }} to={"/"}> <Link style={{ textDecoration: 'none' }} to={"/"}>
<HeaderbarTitle title={context.texts.Mancala} color={textColorOnAppBar} /> <HeaderbarTitle title={t("Mancala")} color={textColorOnAppBar} />
</Link> </Link>
</Row> </Row>
<Row> <Row>
@ -281,7 +284,7 @@ const GamePage: FunctionComponent<{
<Button <Button
context={context} context={context}
color={context.themeManager.theme.pitColor} color={context.themeManager.theme.pitColor}
text={renderNewGameBtn ? context.texts.NewGame : context.texts.Leave} text={renderNewGameBtn ? t("NewGame") : t("Leave")}
onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} /> onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} />
</Row> </Row>
</HeaderBar>; </HeaderBar>;

View File

@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
import { FunctionComponent, useEffect, useState } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { getColorByBrightness } from "../util/ColorUtil"; import { getColorByBrightness } from "../util/ColorUtil";
import { Theme } from "../theme/Theme"; import { Theme } from "@mancala/core";
import HeaderBar from "../components/headerbar/HeaderBar"; import HeaderBar from "../components/headerbar/HeaderBar";
import PageContainer from "../components/PageContainer"; import PageContainer from "../components/PageContainer";
import Row from "../components/Row"; import Row from "../components/Row";
@ -12,6 +12,7 @@ import Button from "../components/Button";
import { Context } from "../context/context"; import { Context } from "../context/context";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import Util from "../util/Util"; import Util from "../util/Util";
import { useTranslation } from "react-i18next";
const Home: FunctionComponent<{ const Home: FunctionComponent<{
context: Context, context: Context,
@ -21,8 +22,10 @@ const Home: FunctionComponent<{
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useTranslation();
const onNewGameClick = () => { const onNewGameClick = () => {
if(Util.checkConnectionAndMaybeAlert(context)) return; if(Util.checkConnectionAndMaybeAlert(context, t)) return;
navigate("/loby") navigate("/loby")
}; };
@ -39,7 +42,7 @@ const Home: FunctionComponent<{
<HeaderbarIcon /> <HeaderbarIcon />
</Link> </Link>
<Link style={{ textDecoration: 'none' }} to={"/"}> <Link style={{ textDecoration: 'none' }} to={"/"}>
<HeaderbarTitle title={context.texts.Mancala} color={textColorOnAppBar} /> <HeaderbarTitle title={t("Mancala")} color={textColorOnAppBar} />
</Link> </Link>
</Row> </Row>
<Row> <Row>
@ -47,7 +50,7 @@ const Home: FunctionComponent<{
<Button <Button
context={context} context={context}
color={context.themeManager.theme.pitColor} color={context.themeManager.theme.pitColor}
text={context.texts.NewGame} text={t("NewGame")}
onClick={onNewGameClick} /> onClick={onNewGameClick} />
</Row> </Row>
</HeaderBar> </HeaderBar>

View File

@ -12,9 +12,10 @@ import PageContainer from '../components/PageContainer';
import Row from '../components/Row'; import Row from '../components/Row';
import { channel_cancel_new_game, channel_on_game_start } from '@mancala/core'; import { channel_cancel_new_game, channel_on_game_start } from '@mancala/core';
import { Context } from '../context/context'; import { Context } from '../context/context';
import { Theme } from '../theme/Theme'; import { Theme } from '@mancala/core';
import { getColorByBrightness } from '../util/ColorUtil'; import { getColorByBrightness } from '../util/ColorUtil';
import Button from "../components/Button"; import Button from "../components/Button";
import { useTranslation } from 'react-i18next';
const LobyPage: FunctionComponent<{ const LobyPage: FunctionComponent<{
context: Context, context: Context,
@ -24,6 +25,8 @@ const LobyPage: FunctionComponent<{
let navigate = useNavigate(); let navigate = useNavigate();
const { t } = useTranslation();
const onGameStart = (message: Object) => { const onGameStart = (message: Object) => {
const newGame: CommonMancalaGame = message as CommonMancalaGame; const newGame: CommonMancalaGame = message as CommonMancalaGame;
navigate(`/game/${newGame.id}`) navigate(`/game/${newGame.id}`)
@ -61,7 +64,7 @@ const LobyPage: FunctionComponent<{
<HeaderbarIcon /> <HeaderbarIcon />
</Link> </Link>
<Link style={{ textDecoration: 'none' }} to={"/"}> <Link style={{ textDecoration: 'none' }} to={"/"}>
<HeaderbarTitle title={context.texts.Mancala} color={textColorOnAppBar} /> <HeaderbarTitle title={t("Mancala")} color={textColorOnAppBar} />
</Link> </Link>
</Row> </Row>
<Row> <Row>
@ -75,12 +78,12 @@ const LobyPage: FunctionComponent<{
alignContent: "center" alignContent: "center"
}}> }}>
<CircularPanel color={context.themeManager.theme.boardColor}> <CircularPanel color={context.themeManager.theme.boardColor}>
<h4 style={{ margin: "0", color: textColorOnBoard }}>{`${context.texts.SearchingOpponent} ${context.texts.PleaseWait}...`}</h4> <h4 style={{ margin: "0", color: textColorOnBoard }}>{`${t("SearchingOpponent")} ${t("PleaseWait")}...`}</h4>
</CircularPanel> </CircularPanel>
<Button <Button
context={context} context={context}
color={context.themeManager.theme.boardColor} color={context.themeManager.theme.boardColor}
text={context.texts.Cancel} text={t("Cancel")}
onClick={onCancelNewGameClick} /> onClick={onCancelNewGameClick} />
</div> </div>

View File

@ -8,8 +8,9 @@ import ThemeSwitchMenu from '../components/headerbar/ThemeSwitchMenu';
import PageContainer from '../components/PageContainer'; import PageContainer from '../components/PageContainer';
import Row from '../components/Row'; import Row from '../components/Row';
import { Context } from '../context/context'; import { Context } from '../context/context';
import { Theme } from '../theme/Theme'; import { Theme } from '@mancala/core';
import { getColorByBrightness } from '../util/ColorUtil'; import { getColorByBrightness } from '../util/ColorUtil';
import { useTranslation } from 'react-i18next';
const PrivacyPage: FunctionComponent<{ const PrivacyPage: FunctionComponent<{
context: Context, context: Context,
@ -17,6 +18,8 @@ const PrivacyPage: FunctionComponent<{
theme: Theme theme: Theme
}> = ({ context, userKey, theme }) => { }> = ({ context, userKey, theme }) => {
const { t } = useTranslation();
const textColorOnAppBar = getColorByBrightness( const textColorOnAppBar = getColorByBrightness(
context.themeManager.theme.appBarBgColor, context.themeManager.theme.appBarBgColor,
context.themeManager.theme.textColor, context.themeManager.theme.textColor,
@ -35,7 +38,7 @@ const PrivacyPage: FunctionComponent<{
<HeaderbarIcon /> <HeaderbarIcon />
</Link> </Link>
<Link style={{ textDecoration: 'none' }} to={"/"}> <Link style={{ textDecoration: 'none' }} to={"/"}>
<HeaderbarTitle title={context.texts.Mancala} color={textColorOnAppBar} /> <HeaderbarTitle title={t("Mancala")} color={textColorOnAppBar} />
</Link> </Link>
</Row> </Row>
<Row> <Row>

View File

@ -1,5 +1,5 @@
import { Context } from "../context/context"; import { Context } from "../context/context";
import { ConnectionState } from "../rtmt/rtmt"; import { ConnectionState } from "@mancala/core";
import notyf from "./Notyf"; import notyf from "./Notyf";
export default class Util { export default class Util {
@ -11,21 +11,21 @@ export default class Util {
return ans; return ans;
} }
public static checkConnectionAndMaybeAlert(context: Context): boolean { public static checkConnectionAndMaybeAlert(context: Context, t: (text: string) => string): boolean {
if (context.rtmt.connectionState !== "connected") { if (context.rtmt.connectionState !== "connected") {
notyf.error(context.texts.ConnectionLost); notyf.error(t("ConnectionLost"));
return true; return true;
} }
return false; return false;
} }
public static getTextByConnectionState(context: Context, connectionState: ConnectionState): string { public static getTextByConnectionState(context: Context, connectionState: ConnectionState, t: (text: string) => string): string {
const map: { [key: string]: string } = { const map: { [key: string]: string } = {
connecting: context.texts.Connecting, connecting: t("Connecting"),
connected: context.texts.Connected, connected: t("Connected"),
error: context.texts.CannotConnect, error: t("CannotConnect"),
closed: context.texts.ConnectingAgain, closed: t("ConnectingAgain"),
reconnecting: context.texts.ConnectingAgain, reconnecting: t("ConnectingAgain"),
}; };
return map[connectionState]; return map[connectionState];
} }

View File

@ -1,42 +0,0 @@
export default {
translation: {
Mancala: "Mancala",
Leave: "Leave The Game",
NewGame: "New Game",
YourTurn: "Your Turn",
OpponentTurn: "Opponent Turn",
GameEnded: "Game Ended",
InternalErrorOccurred: "An internal error has occurred",
YouWon: "You Won",
Won: "Won",
YouLost: "You Lost",
Connecting: "Connecting",
Connected: "Connected",
CannotConnect: "Can't Connect",
ConnectionLost: "Network Connection Lost",
ConnectingAgain: "Connecting Again",
ServerError: "Server Error",
SearchingOpponet: "Searching Opponet",
OpponentLeftTheGame: "Opponent Leaves The Game",
YouLeftTheGame: "You Left The Game",
UserLeftTheGame: "Left The Game",
SearchingOpponent: "Searching Opponent",
PleaseWait: "Please Wait",
GameDraw: "Draw",
Anonymous: "Anonymous",
GameNotFound: "Game Not Found",
Loading: "Loading",
Playing: "Playing",
Error: "Error",
ErrorWhenRetrievingInformation: "An error occured when retrieving information!",
UCanOnlyPlayYourOwnPits: "You can only play your own pits",
UMustWaitUntilCurrentMoveComplete: "You must wait until the current move is complete",
UCanNotPlayEmptyPit: "You can not play empty pit",
AreYouSureToLeaveGame: "Are you sure to leave game?",
Yes: "Yes",
Cancel: "Cancel",
Help: "Help",
WebApp: "Goto WebApp",
Privacy: "Open Privacy Policy "
}
};

View File

@ -2,11 +2,14 @@
import i18n from 'i18next'; import i18n from 'i18next';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import en from './en'; import { en, tr } from '@mancala/core';
import tr from './tr';
const resources = { // list of languages const resources = { // list of languages
en, en: {
tr, translation: en
},
tr: {
translation: tr
}
}; };
i18n.use(initReactI18next) // passes i18n down to react-i18next i18n.use(initReactI18next) // passes i18n down to react-i18next
.init({ .init({

View File

@ -1,42 +0,0 @@
export default {
translation: {
Mancala: "Köçürme",
Leave: "Oyundan Ayrıl",
NewGame: "Yeni Oyun",
YourTurn: "Sıra Sende",
OpponentTurn: "Sıra Rakipte",
GameEnded: "Oyun Bitti",
InternalErrorOccurred: "Dahili bir hata oluştu",
YouWon: "Kazandın",
Won: "Kazandı",
YouLost: "Kaybettin",
Connecting: "Bağlanılıyor",
Connected: "Bağlandı",
CannotConnect: "Bağlanılamadı",
ConnectionLost: "Ağ Bağlantısı Koptu",
ConnectingAgain: "Tekrar Bağlanılıyor",
ServerError: "Sunucu Hatası",
SearchingOpponet: "Rakip Aranıyor",
OpponentLeftTheGame: "Rakip Oyundan Ayrıldı",
YouLeftTheGame: "Sen Oyundan Ayrıldın",
UserLeftTheGame: "Oyundan Ayrıldı",
SearchingOpponent: "Rakip Aranıyor",
PleaseWait: "Lütfen Bekleyin",
GameDraw: "Berabere",
Anonymous: "Anonim",
GameNotFound: "Oyun Bulunamadı",
Loading: "Yükleniyor",
Playing: "Oynuyor",
Error: "Hata",
ErrorWhenRetrievingInformation: "Bilgiler toplanırken bir hata oluştu!",
UCanOnlyPlayYourOwnPits: "Sadece sana ait olan kuyular ile oynayabilirsin",
UMustWaitUntilCurrentMoveComplete: "Devam eden haraketin bitmesini beklemelisin",
UCanNotPlayEmptyPit: "Boş kuyu ile oynayamazsın",
AreYouSureToLeaveGame: "Oyundan ayrılmak istediğine emin misin?",
Yes: "Evet",
Cancel: "İptal",
Help: "Yardım",
WebApp: "Web Uygulamasına Git",
Privacy: "Gizlilik Politikasını Göster"
}
};