Merged PR 6: タスク 1484: 言語切り替えの仕組みをいれる
## 概要 [タスク 1484: 言語切り替えの仕組みをいれる](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_workitems/edit/1484) - トップ画面での言語切り替え機能を実装しました。 - 英語、ドイツ語、フランス語、スペイン語で切り替えできるようにしています。 ## レビューポイント - 言語切り替えとして機能に不足はないか - デザインは仮組なので対象外 - コンボボックスで言語切り替えできるところのみ確認をお願いします。 ## UIの変更 - 言語切り替え追加 - [Task1484](https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task1484?csf=1&web=1&e=e3lu7p) ## 動作確認状況 - 画面上で言語切り替えできることを確認 ## 補足 - デザインはタグだけの仮組ですので無視してください。
This commit is contained in:
parent
7329591b6f
commit
8822ddaee4
1459
dictation_client/package-lock.json
generated
1459
dictation_client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -51,7 +51,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.6",
|
||||
"@mdx-js/react": "^2.1.2",
|
||||
"@openapitools/openapi-generator-cli": "^2.5.1",
|
||||
"@openapitools/openapi-generator-cli": "^0.0.6",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/luxon": "^3.2.0",
|
||||
"@types/react": "^18.0.0",
|
||||
|
||||
@ -8,10 +8,12 @@ import { useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import globalAxios, { AxiosError, AxiosResponse } from "axios";
|
||||
import { clearToken } from "features/auth";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const App = (): JSX.Element => {
|
||||
const dispatch = useDispatch();
|
||||
const { instance } = useMsal();
|
||||
const [t, i18n] = useTranslation();
|
||||
const pca = new PublicClientApplication(msalConfig);
|
||||
useEffect(() => {
|
||||
const id = globalAxios.interceptors.response.use(
|
||||
@ -32,6 +34,18 @@ const App = (): JSX.Element => {
|
||||
return cleanup;
|
||||
}, [dispatch, instance]);
|
||||
|
||||
// Language読み取り
|
||||
useEffect(() => {
|
||||
const language = document.cookie
|
||||
.split(";")
|
||||
.map((x) => x.split("="))
|
||||
.find((x) => x.length === 2 && x[0] === "language");
|
||||
|
||||
if (language) {
|
||||
i18n.changeLanguage(language[1]);
|
||||
}
|
||||
}, [i18n]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
|
||||
67
dictation_client/src/assets/images/OMS_logo_black.svg
Normal file
67
dictation_client/src/assets/images/OMS_logo_black.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 362.41 42" style="enable-background:new 0 0 362.41 42;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{clip-path:url(#SVGID_00000106112683229958979730000004837138376666885508_);}
|
||||
.st1{clip-path:url(#SVGID_00000100361991382501253840000013201318416462760885_);}
|
||||
.st2{clip-path:url(#SVGID_00000100361991382501253840000013201318416462760885_);fill-rule:evenodd;clip-rule:evenodd;}
|
||||
</style>
|
||||
<g id="OM_System_Black_-_One_Line_-_RGB_00000168110105817326191910000010615373263908845735_">
|
||||
<g>
|
||||
<defs>
|
||||
<rect id="SVGID_1_" y="0" width="362.41" height="42"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_00000097489272735381870970000015716550008865822343_">
|
||||
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
<g style="clip-path:url(#SVGID_00000097489272735381870970000015716550008865822343_);">
|
||||
<defs>
|
||||
<rect id="SVGID_00000084515526535015927220000011441361349608841099_" y="0" width="362.41" height="42"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_00000005241618409923234440000016758378329632926897_">
|
||||
<use xlink:href="#SVGID_00000084515526535015927220000011441361349608841099_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
<path style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);" d="M62.35,23.65l-9.06-22.2H41.24
|
||||
v15.26C39.28,7.17,30.89,0,20.84,0C9.33,0,0,9.4,0,21c0,11.59,9.33,21,20.84,21c10.05,0,18.44-7.17,20.4-16.71v15.07h9.45V19.18
|
||||
l8.07,21.18h7.19l8.07-21.18v21.18h9.45V1.45H71.42L62.35,23.65z M20.84,32.57C14.5,32.57,9.36,27.39,9.36,21
|
||||
S14.5,9.43,20.84,9.43S32.33,14.61,32.33,21S27.19,32.57,20.84,32.57"/>
|
||||
|
||||
<path style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);fill-rule:evenodd;clip-rule:evenodd;" d="
|
||||
M127.89,17.47h9.94c0,0,0-2.32-0.27-3.9c-0.27-1.58-0.84-3.09-2.45-4.37c-1.62-1.27-5.01-1.81-6.65-2.11
|
||||
c-1.65-0.3-7.16-0.4-10.55-0.4s-8.27,0.29-10.37,0.73c-2.06,0.43-4.88,1.77-5.96,3.5c-0.75,1.18-1.85,3.52-1.85,6.13
|
||||
c0,2.65,0.36,5.14,1.09,6.45c0.71,1.26,1.41,2.03,2.58,2.7c1.18,0.67,3.44,1.01,6.32,1.14c2.85,0.13,6.22,0.19,8.93,0.27
|
||||
c2.75,0.09,6.41,0.25,7.89,0.36c1.63,0.12,2.35,1.15,2.35,2.19c0,1.05-0.5,1.83-1.54,2.19c-1.16,0.39-5.29,0.35-8.01,0.38
|
||||
c-2.65,0.04-6.03-0.14-7.64-0.32c-1.34-0.15-2.16-1.23-2.32-2.77h-9.85c0,0,0.02,2.8,0.32,4.34c0.3,1.54,1.01,2.72,1.85,3.66
|
||||
c0.84,0.94,1.68,1.41,3.05,1.85c1.38,0.44,7.76,0.87,13.5,0.87c5.74,0,10.58-0.07,12.43-0.37c1.84-0.31,3.86-0.98,4.93-1.82
|
||||
c1.08-0.84,2.2-2.25,2.84-3.76c0.48-1.12,0.72-2.99,0.69-4.83c-0.04-1.85-0.57-4.87-1.54-6.18c-0.98-1.31-1.58-2.08-3.12-2.66
|
||||
c-1.55-0.57-4.16-0.91-5.81-0.97c-1.64-0.07-6.95-0.23-9.54-0.3c-2.11-0.05-6.6-0.24-7.62-0.47c-0.93-0.21-1.54-0.64-1.54-1.92
|
||||
c0-1.27,0.68-1.73,1.44-1.91c0.99-0.24,3.56-0.66,7.19-0.66c3.63,0,6.35,0.25,7.12,0.42c0.77,0.17,1.38,0.57,1.75,1.04
|
||||
C127.82,16.46,127.89,17.47,127.89,17.47"/>
|
||||
<polygon style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);" points="161.22,19.5 171.82,6.96
|
||||
184.35,6.96 166.25,28.53 166.25,40.07 156.35,40.07 156.18,28.53 138.08,6.96 150.6,6.96 "/>
|
||||
<polygon style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);" points="264.47,6.96 264.47,15.68
|
||||
249.99,15.68 249.99,40.06 239.92,40.06 239.92,15.68 225.45,15.68 225.45,6.96 "/>
|
||||
<polygon style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);" points="278.2,32.47 303.38,32.47
|
||||
303.38,40.07 278.2,40.07 268.12,40.07 268.12,6.96 278.2,6.96 303.38,6.96 303.38,14.54 278.2,14.54 278.2,19.71 301.84,19.71
|
||||
301.84,27.31 278.2,27.31 "/>
|
||||
<path style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);" d="M335.48,40.06h-3.85l-12.17-21.95
|
||||
c-0.11-0.21-0.28-0.5-0.53-0.45c-0.31,0.07-0.31,0.43-0.31,0.68v21.73h-10.08V6.96h15.72l11.09,20c0.03,0.06,0.06,0.09,0.12,0.08
|
||||
c0.05,0.01,0.09-0.02,0.12-0.08l11.09-20h15.72v33.11h-10.07V18.34c0-0.25,0-0.61-0.31-0.68c-0.24-0.05-0.41,0.25-0.52,0.45
|
||||
l-12.17,21.95H335.48z"/>
|
||||
|
||||
<path style="clip-path:url(#SVGID_00000005241618409923234440000016758378329632926897_);fill-rule:evenodd;clip-rule:evenodd;" d="
|
||||
M212.27,17.47h9.94c0,0,0-2.32-0.27-3.9c-0.27-1.58-0.84-3.09-2.45-4.37c-1.61-1.27-5.01-1.81-6.65-2.11
|
||||
c-1.65-0.3-7.15-0.4-10.54-0.4c-3.39,0-8.27,0.29-10.37,0.73c-2.06,0.43-4.88,1.77-5.97,3.5c-0.75,1.18-1.85,3.52-1.85,6.13
|
||||
c0,2.65,0.36,5.14,1.09,6.45c0.71,1.26,1.41,2.03,2.58,2.7c1.18,0.67,3.44,1.01,6.32,1.14c2.85,0.13,6.22,0.19,8.93,0.27
|
||||
c2.76,0.09,6.42,0.25,7.9,0.36c1.63,0.12,2.35,1.15,2.35,2.19c0,1.05-0.51,1.83-1.55,2.19c-1.15,0.39-5.29,0.35-8.01,0.38
|
||||
c-2.65,0.04-6.03-0.14-7.64-0.32c-1.33-0.15-2.17-1.23-2.32-2.77h-9.85c0,0,0.02,2.8,0.32,4.34c0.3,1.54,1.01,2.72,1.85,3.66
|
||||
c0.84,0.94,1.68,1.41,3.05,1.85c1.38,0.44,7.76,0.87,13.5,0.87c5.74,0,10.57-0.07,12.42-0.37c1.84-0.31,3.86-0.98,4.93-1.82
|
||||
c1.08-0.84,2.2-2.25,2.84-3.76c0.48-1.12,0.72-2.99,0.69-4.83c-0.04-1.85-0.58-4.87-1.54-6.18c-0.98-1.31-1.58-2.08-3.12-2.66
|
||||
c-1.55-0.57-4.16-0.91-5.81-0.97c-1.64-0.07-6.95-0.23-9.54-0.3c-2.11-0.05-6.6-0.24-7.62-0.47c-0.93-0.21-1.55-0.64-1.55-1.92
|
||||
c0-1.27,0.69-1.73,1.45-1.91c0.99-0.24,3.56-0.66,7.19-0.66c3.63,0,6.35,0.25,7.12,0.42c0.77,0.17,1.38,0.57,1.75,1.04
|
||||
C212.2,16.46,212.27,17.47,212.27,17.47"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
7
dictation_client/src/assets/images/arrow_forward.svg
Normal file
7
dictation_client/src/assets/images/arrow_forward.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_レイヤー_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fff;}</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="m16,32l-2.1-2.1,12.4-12.4H0v-3h26.2L13.9,2.1l2.1-2.1,16,16-16,16Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 277 B |
11
dictation_client/src/assets/images/arrow_forward_blue.svg
Normal file
11
dictation_client/src/assets/images/arrow_forward_blue.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="_レイヤー_1" data-name="レイヤー 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #004086;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="cls-1" d="m16,32l-2.1-2.1,12.4-12.4H0v-3h26.2L13.9,2.1l2.1-2.1,16,16-16,16Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 338 B |
8
dictation_client/src/features/top/constants.ts
Normal file
8
dictation_client/src/features/top/constants.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { getTranslationID } from "../../translation";
|
||||
|
||||
export const LANGUAGE_LIST = [
|
||||
{ value: "en", label: getTranslationID("topPage.label.languageEnglish") },
|
||||
{ value: "de", label: getTranslationID("topPage.label.languageGerman") },
|
||||
{ value: "fr", label: getTranslationID("topPage.label.languageFrench") },
|
||||
{ value: "es", label: getTranslationID("topPage.label.languageSpanish") },
|
||||
];
|
||||
29
dictation_client/src/i18n.ts
Normal file
29
dictation_client/src/i18n.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import en from "./translation/en.json";
|
||||
import es from "./translation/es.json";
|
||||
import fr from "./translation/fr.json";
|
||||
import de from "./translation/de.json";
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: {
|
||||
en: {
|
||||
translation: en,
|
||||
},
|
||||
de: {
|
||||
translation: de,
|
||||
},
|
||||
fr: {
|
||||
translation: fr,
|
||||
},
|
||||
es: {
|
||||
translation: es,
|
||||
},
|
||||
},
|
||||
lng: "en",
|
||||
defaultNS: "translation",
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
export default i18n;
|
||||
@ -1,9 +1,11 @@
|
||||
import { store } from "app/store";
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import { Provider } from "react-redux";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import i18n from "./i18n";
|
||||
|
||||
const container = document.getElementById("root");
|
||||
if (container) {
|
||||
@ -11,6 +13,7 @@ if (container) {
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18n} />
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>
|
||||
|
||||
@ -1,18 +1,72 @@
|
||||
import { useMsal } from "@azure/msal-react";
|
||||
import { loginRequest } from "common/msalConfig";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getTranslationID } from "translation";
|
||||
import { LANGUAGE_LIST } from "../../features/top/constants";
|
||||
|
||||
const TopPage: React.FC = (): JSX.Element => {
|
||||
const { instance } = useMsal();
|
||||
// eslint-disable-next-line
|
||||
const [t, i18n] = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => instance.loginRedirect(loginRequest)}
|
||||
<header>
|
||||
<div>
|
||||
<img src="../../assets/images/OMS_logo_black.svg" alt="OM System" />
|
||||
</div>
|
||||
<p>ODMS Cloud</p>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section>
|
||||
<div>
|
||||
<dl>
|
||||
<dt>{t(getTranslationID("topPage.label.displayLanguage"))}</dt>
|
||||
<dd>
|
||||
<select
|
||||
onChange={(e) => {
|
||||
i18n.changeLanguage(e.currentTarget.value);
|
||||
// 既にcookieに選択言語があれば削除
|
||||
document.cookie = "language=; max-age=0";
|
||||
// cookieの期限は1年
|
||||
document.cookie = `language=${e.currentTarget.value}; max-age=31536000`;
|
||||
}}
|
||||
>
|
||||
sign in
|
||||
</button>
|
||||
{LANGUAGE_LIST.map((x) => (
|
||||
<option key={x.value} value={x.value}>
|
||||
{t(x.label)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</dd>
|
||||
<dt>Already have an account?</dt>
|
||||
<dd>
|
||||
{/* eslint-disable */}
|
||||
<a onClick={() => instance.loginRedirect(loginRequest)}>
|
||||
Sign in
|
||||
<img src="../../assets/images/arrow_forward.svg" alt="" />
|
||||
</a>
|
||||
</dd>
|
||||
<dt>New user?</dt>
|
||||
<dd>
|
||||
<a href="●●">
|
||||
Create a new account
|
||||
<img
|
||||
src="../../assets/images/arrow_forward_blue.svg"
|
||||
alt=""
|
||||
/>
|
||||
</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<div>© OM Digital Solutions 2023</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
11
dictation_client/src/translation/de.json
Normal file
11
dictation_client/src/translation/de.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"topPage": {
|
||||
"label": {
|
||||
"displayLanguage": "(de)Display Language",
|
||||
"languageEnglish": "(de)English",
|
||||
"languageGerman": "(de)German",
|
||||
"languageFrench": "(de)French",
|
||||
"languageSpanish": "(de)Spanish"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
dictation_client/src/translation/en.json
Normal file
11
dictation_client/src/translation/en.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"topPage": {
|
||||
"label": {
|
||||
"displayLanguage": "Display Language",
|
||||
"languageEnglish": "English",
|
||||
"languageGerman": "German",
|
||||
"languageFrench": "French",
|
||||
"languageSpanish": "Spanish"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
dictation_client/src/translation/es.json
Normal file
11
dictation_client/src/translation/es.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"topPage": {
|
||||
"label": {
|
||||
"displayLanguage": "(es)Display Language",
|
||||
"languageEnglish": "(es)English",
|
||||
"languageGerman": "(es)German",
|
||||
"languageFrench": "(es)French",
|
||||
"languageSpanish": "(es)Spanish"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
dictation_client/src/translation/fr.json
Normal file
11
dictation_client/src/translation/fr.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"topPage": {
|
||||
"label": {
|
||||
"displayLanguage": "(fr)Display Language",
|
||||
"languageEnglish": "(fr)English",
|
||||
"languageGerman": "(fr)German",
|
||||
"languageFrench": "(fr)French",
|
||||
"languageSpanish": "(fr)Spanish"
|
||||
}
|
||||
}
|
||||
}
|
||||
17
dictation_client/src/translation/index.ts
Normal file
17
dictation_client/src/translation/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import en from "./en.json";
|
||||
|
||||
// 再帰的にプロパティ名リテラル型の結合を行う
|
||||
// Pを列挙 => key-remapping(P)でPを元に(P.Pの子要素).(左の子要素)...という新しいプロパティ名を再帰的に作成する
|
||||
type Key<T> = keyof {
|
||||
[P in keyof T as T[P] extends string
|
||||
? P
|
||||
: Key<T[P]> extends string
|
||||
? `${P extends string ? P : never}.${Key<T[P]>}`
|
||||
: never]: T[P];
|
||||
};
|
||||
|
||||
// IDの特定はen.jsonを基準とする
|
||||
export type TranslationID = Key<typeof en>;
|
||||
|
||||
// ユーザーの入力補助用の関数
|
||||
export const getTranslationID = (id: TranslationID) => id;
|
||||
Loading…
x
Reference in New Issue
Block a user