Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i18n #241

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

i18n #241

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { labelColor, DESKTOP_MIN_WIDTH, media } from "../styles"
import { appVersion } from "../util/appVersion"
import { TimeMachine } from "./TimeMachine"
import ContentWrapper from "./ContentWrapper"
import { __, setLanguage } from "./InternationalizedText"

export default function Footer() {
// @todo #1 Polish the footer
Expand All @@ -15,6 +16,9 @@ export default function Footer() {
padding: "2rem 0.5rem 4rem",
marginTop: "1rem",
color: labelColor,
a: {
color: "#666",
},
}}
>
<ContentWrapper>
Expand All @@ -35,9 +39,17 @@ export default function Footer() {
href="https://github.com/codeforthailand/election-live"
target="_blank"
>
<FontAwesomeIcon icon={faGithub} /> Github
<FontAwesomeIcon icon={faGithub} /> GitHub
</a>
<br />v{appVersion}
<br />v{appVersion} - View this page in{" "}
{__(
<a href="javascript:" onClick={() => setLanguage("en")}>
English
</a>,
<a href="javascript:" onClick={() => setLanguage("th")}>
Thai
</a>
)}
</div>
</footer>
)
Expand Down
69 changes: 69 additions & 0 deletions src/components/InternationalizedText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useState, useEffect } from "react"
import _ from "lodash"
import { Debug } from "../util/Debug"
import { EventEmitter } from "events"

const strings = {
ดูผลตามพื้นที่: "View by Area",
ดูผลตามพรรค: "View by Party",
ลองตั้งรัฐบาล: "Election Simulator",
"เกี่ยวกับ ELECT Live": "About ELECT Live!",

เขตพื้นที่: "Filter by Region",
ตัวเลือกพิเศษ: "Special Filters",
}

/** @typedef {'th' | 'en'} Language */
const debug = Debug("elect:l10n")
const languageChangeEmitter = new EventEmitter()
languageChangeEmitter.setMaxListeners(10000)
let currentLanguage = /** @type {Language} */ ("th")
let loaded = false

export function InternationalizedText({ thai, english }) {
const [language, setLanguage] = useState(currentLanguage)
useEffect(() => {
const listener = () => setLanguage(currentLanguage)
languageChangeEmitter.on("change", listener)
return () => languageChangeEmitter.removeListener("change", listener)
}, [])
useEffect(() => {
if (loaded) return
try {
const targetLanguage = localStorage.ELECT_LANGUAGE === "en" ? "en" : "th"
requestAnimationFrame(() => {
debug("Set language to", targetLanguage, "from localStorage")
if (currentLanguage !== targetLanguage) {
currentLanguage = targetLanguage
languageChangeEmitter.emit("change")
}
})
} catch (e) {
debug("Cannot load language from localStorage", e)
} finally {
loaded = true
}
}, [])
return <span>{language === "en" ? english : thai}</span>
}

export function __(thai) {
const english = strings[thai] || thai
return <InternationalizedText thai={thai} english={english} />
}

/**
* @param {Language} lang
*/
export function setLanguage(lang = "th") {
currentLanguage = lang
try {
if (localStorage.ELECT_LANGUAGE !== lang) {
localStorage.ELECT_LANGUAGE = lang
debug("Persisted language", lang, "to localStorage")
}
} catch (e) {
debug("Cannot persist language to", lang, e)
}
languageChangeEmitter.emit("change")
}
3 changes: 2 additions & 1 deletion src/components/LinkToSimulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { WIDE_NAV_MIN_WIDTH, media } from "../styles"
import { nationwidePartyStatsFromSummaryJSON } from "../models/PartyStats"
import { useSummaryData } from "../models/LiveDataSubscription"
import createSimulatorUrl from "../util/createSimulatorUrl"
import { __ } from "./InternationalizedText"

const stylesLink = {
color: "#FFFFFF",
Expand Down Expand Up @@ -50,7 +51,7 @@ export default function LinkToSimulator() {
window.open(url, "_blank")
}}
>
ลองตั้งรัฐบาล
{__("ลองตั้งรัฐบาล")}
</Link>
)
}
7 changes: 4 additions & 3 deletions src/components/NavBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import { Link } from "gatsby"
import { WIDE_NAV_MIN_WIDTH, media } from "../styles"
import _ from "lodash"
import LinkToSimulator from "./LinkToSimulator"
import { __ } from "./InternationalizedText"

const menues = [
{
name: "by-area",
route: "/",
label: "ดูผลตามพื้นที่",
label: __("ดูผลตามพื้นที่"),
},
{
name: "by-party",
route: "/party",
label: "ดูผลตามพรรค",
label: __("ดูผลตามพรรค"),
},
// {
// name: "overview",
Expand All @@ -23,7 +24,7 @@ const menues = [
{
name: "about",
route: "/about",
label: "เกี่ยวกับ ELECT Live",
label: __("เกี่ยวกับ ELECT Live"),
},
]

Expand Down
16 changes: 13 additions & 3 deletions src/components/ZoneFilterPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "../models/information"
import { trackEvent } from "../util/analytics"
import HelpTooltip from "./HelpTooltip"
import { __, InternationalizedText } from "./InternationalizedText"

export const ZoneFilterContext = createContext(
/** @type {ZoneFilterName} */ ("all")
Expand All @@ -25,7 +26,7 @@ export function ZoneFilterPanel({ onFilterSelect, autoFocus }) {

return (
<div>
<div css={{ fontSize: 20, fontWeight: "bold" }}>เขตพื้นที่</div>
<div css={{ fontSize: 20, fontWeight: "bold" }}>{__("เขตพื้นที่")}</div>
<ul css={{ padding: 0, listStyle: "none" }}>
{renderFilter("all")}
{renderFilter("northern")}
Expand All @@ -34,7 +35,9 @@ export function ZoneFilterPanel({ onFilterSelect, autoFocus }) {
{renderFilter("southern")}
{renderFilterSearch()}
</ul>
<div css={{ fontSize: 20, fontWeight: "bold" }}>ตัวเลือกพิเศษ</div>
<div css={{ fontSize: 20, fontWeight: "bold" }}>
{__("ตัวเลือกพิเศษ")}
</div>
<ul css={{ padding: 0, listStyle: "none" }}>
{renderFilter("urban")}
{renderFilter("rural")}
Expand Down Expand Up @@ -130,7 +133,14 @@ export function ZoneFilterPanel({ onFilterSelect, autoFocus }) {
{!current && <FontAwesomeIcon icon={faCircle} />}
</span>
<span>
{label.length > 0 ? label : areaFilters[filterName].name.th}
{label.length > 0 ? (
label
) : (
<InternationalizedText
thai={areaFilters[filterName].name.th}
english={areaFilters[filterName].name.en}
/>
)}
</span>
{areaFilters[filterName].description ? (
<HelpTooltip
Expand Down
8 changes: 7 additions & 1 deletion src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
partyStatsFromSummaryJSON,
partyStatsRowTotalSeats,
} from "../models/PartyStats"
import { __, InternationalizedText } from "../components/InternationalizedText"

export const MobileTabContext = createContext(
/** @type {import('../components/ZoneMasterView').MobileTab} */ ("summary")
Expand Down Expand Up @@ -103,7 +104,12 @@ function SummaryHeaderContainer({ filterName }) {
const currentFilter = filters[filterName]
const totalZoneCount = zones.filter(zone => checkFilter(currentFilter, zone))
.length
const title = currentFilter.name.th
const title = (
<InternationalizedText
thai={currentFilter.name.th}
english={currentFilter.name.en}
/>
)

if (!summaryState.completed) {
return (
Expand Down