mirror of
https://github.com/bellingcat/ukraine-timemap.git
synced 2026-06-18 16:28:34 +03:00
Add LanguageSwitch component for changing app's language (#25)
Co-authored-by: msramalho <19508417+msramalho@users.noreply.github.com>
This commit is contained in:
@@ -93,7 +93,8 @@
|
||||
"warning": "(!) HECHOS CUESTIONADOS"
|
||||
}
|
||||
},
|
||||
"en-US": {
|
||||
"en": {
|
||||
"language_short": "Eng",
|
||||
"tiles": {
|
||||
"default": "Map",
|
||||
"satellite": "Sat"
|
||||
@@ -192,5 +193,11 @@
|
||||
"receiver": "Receiver",
|
||||
"warning": "(!) Highly questioned"
|
||||
}
|
||||
},
|
||||
"ru": {
|
||||
"language_short": "Рус"
|
||||
},
|
||||
"uk": {
|
||||
"language_short": "Укр"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ let { DATE_FMT, TIME_FMT } = process.env;
|
||||
if (!DATE_FMT) DATE_FMT = "MM/DD/YYYY";
|
||||
if (!TIME_FMT) TIME_FMT = "HH:mm";
|
||||
|
||||
export const language = process.env.store.app.language || "en-US";
|
||||
export const language = process.env.store.app.language || "en";
|
||||
|
||||
export function getPathLeaf(path) {
|
||||
const splitPath = path.split("/");
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from "../common/utilities.js";
|
||||
import { ToolbarButton } from "./controls/atoms/ToolbarButton";
|
||||
import { FullscreenToggle } from "./controls/FullScreenToggle";
|
||||
import { LanguageSwitch } from "./controls/LanguageSwitch";
|
||||
|
||||
class Toolbar extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -274,6 +275,15 @@ class Toolbar extends React.Component {
|
||||
<div className="toolbar-header" onClick={this.props.methods.onTitle}>
|
||||
<p>{title}</p>
|
||||
</div>
|
||||
<div className="toolbar-languages">
|
||||
<LanguageSwitch
|
||||
language={this.props.language}
|
||||
languages={this.props.languages}
|
||||
actions={{
|
||||
toggleLanguage: this.props.actions.toggleLanguage,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="toolbar-tabs">
|
||||
<TabList>
|
||||
{narrativesExist
|
||||
@@ -347,6 +357,7 @@ function mapStateToProps(state) {
|
||||
narratives: selectors.selectNarratives(state),
|
||||
shapes: selectors.getShapes(state),
|
||||
language: state.app.language,
|
||||
languages: state.app.languages,
|
||||
toolbarCopy: state.app.toolbar,
|
||||
activeFilters: selectors.getActiveFilters(state),
|
||||
activeCategories: selectors.getActiveCategories(state),
|
||||
|
||||
@@ -84,7 +84,7 @@ export const Card = ({
|
||||
onSelect = () => {},
|
||||
sources = [],
|
||||
isSelected = false,
|
||||
language = "en-US",
|
||||
language = "en",
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const toggle = () => setIsOpen(!isOpen);
|
||||
|
||||
24
src/components/controls/LanguageSwitch.js
Normal file
24
src/components/controls/LanguageSwitch.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { createElement } from "react";
|
||||
import copy from "../../common/data/copy.json";
|
||||
|
||||
export function LanguageSwitch({
|
||||
language: currentLanguage,
|
||||
languages,
|
||||
actions: { toggleLanguage },
|
||||
}) {
|
||||
if (!languages || languages.length <= 1) return null;
|
||||
return createElement("div", {
|
||||
className: "language-switch",
|
||||
onClick: () => toggleLanguage(),
|
||||
children: languages.map((language) =>
|
||||
createElement("span", {
|
||||
key: language,
|
||||
className:
|
||||
language !== currentLanguage
|
||||
? "language-option"
|
||||
: "language-option selected",
|
||||
children: copy[language].language_short,
|
||||
})
|
||||
),
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,14 @@ import ReactDOM from "react-dom/client";
|
||||
import { Provider } from "react-redux";
|
||||
import store from "./store";
|
||||
import App from "./components/App";
|
||||
import copy from "./common/data/copy.json";
|
||||
|
||||
// XXX: Hack to make migration from "copy.json" and
|
||||
// adding missing translation strings smoother.
|
||||
Object.assign(copy, {
|
||||
ru: { ...copy["en"], ...copy["uk"], ...copy["ru"] },
|
||||
uk: { ...copy["en"], ...copy["ru"], ...copy["uk"] },
|
||||
});
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("explore-app"));
|
||||
root.render(
|
||||
@@ -11,6 +19,21 @@ root.render(
|
||||
</Provider>
|
||||
);
|
||||
|
||||
store.subscribe(() => {
|
||||
const { app } = store.getState();
|
||||
renderAppLanguage(app);
|
||||
});
|
||||
|
||||
// Update language in places that are out of the App's reach
|
||||
function renderAppLanguage({ language }) {
|
||||
const html = document.documentElement;
|
||||
if (language && language !== html.lang) {
|
||||
html.lang = language;
|
||||
const title = process.env.page_title[language];
|
||||
if (title) document.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
// Expressions from https://exceptionshub.com/how-to-detect-safari-chrome-ie-firefox-and-opera-browser.html
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
@@ -227,10 +227,15 @@ function updateDimensions(appState, action) {
|
||||
}
|
||||
|
||||
function toggleLanguage(appState, action) {
|
||||
const otherLanguage = appState.language === "es-MX" ? "en-US" : "es-MX";
|
||||
return Object.assign({}, appState, {
|
||||
language: action.language || otherLanguage,
|
||||
});
|
||||
return {
|
||||
...appState,
|
||||
language: action.language || selectNextLanguage(appState),
|
||||
};
|
||||
function selectNextLanguage({ language, languages }) {
|
||||
const currentIndex = appState.languages.indexOf(language);
|
||||
const nextIndex = (currentIndex + 1) % languages.length;
|
||||
return languages[nextIndex];
|
||||
}
|
||||
}
|
||||
|
||||
function updateSource(appState, action) {
|
||||
|
||||
22
src/scss/languageswitch.scss
Normal file
22
src/scss/languageswitch.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
.language-switch {
|
||||
padding: 1.5em 1em;
|
||||
color: $midwhite;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
.language-option {
|
||||
padding: 0.5em 0.25em;
|
||||
transition: 0.2s ease;
|
||||
border-bottom: solid 2px transparent;
|
||||
&.selected {
|
||||
font-weight: bold;
|
||||
border-bottom-color: $midwhite;
|
||||
color: $offwhite;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.language-option {
|
||||
border-bottom-color: $offwhite;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,3 +14,4 @@
|
||||
@import "cover";
|
||||
@import "search";
|
||||
@import "satelliteoverlaytoggle";
|
||||
@import "languageswitch";
|
||||
|
||||
@@ -66,7 +66,8 @@ const initial = {
|
||||
},
|
||||
},
|
||||
shapes: [],
|
||||
language: "en-US",
|
||||
language: "en",
|
||||
languages: ["en", "ru", "uk"],
|
||||
cluster: {
|
||||
radius: 30,
|
||||
minZoom: 2,
|
||||
|
||||
Reference in New Issue
Block a user