Compare commits
42 Commits
72747015ed
...
resizeSvg
| Author | SHA1 | Date | |
|---|---|---|---|
| 2949c48ed9 | |||
| 6aedbbc942 | |||
| 977cd1aa62 | |||
| 81fe778212 | |||
| bf8825294b | |||
| 6e9e11c41c | |||
| 66d88c133b | |||
| f62697d03a | |||
| ad254d2760 | |||
| 49b4820c2d | |||
| 3d0c20ae48 | |||
| 3d06971e77 | |||
| 50a476edad | |||
| 2814f80fc8 | |||
| 74f3cb9740 | |||
| 2f7cdd2ea7 | |||
| 1fbe5a1abf | |||
| c15bd02c17 | |||
| bf453f9934 | |||
| a4a2451edd | |||
| 56cae83339 | |||
| 53cd9b9465 | |||
| fe2aaef413 | |||
| f26a5b8c59 | |||
| 2d810bb661 | |||
| cf5b2e40a0 | |||
| 0c11a302e3 | |||
| 9cc650a467 | |||
| 0e26b2345a | |||
| 38316b0ea9 | |||
| b2055b11d9 | |||
| f0395763e5 | |||
| 776e45686e | |||
| ab4acd189d | |||
| 93ff0d172e | |||
| f495d939cb | |||
| d8c2b22a46 | |||
| f65ea1dd8f | |||
| da993bf1aa | |||
| 7110b0e0e3 | |||
| d1feca7003 | |||
| 3aa4135a81 |
@@ -7,6 +7,9 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
@@ -24,17 +27,15 @@ type translationStruct struct {
|
||||
Mr string `db:"marathi" json:"mr"`
|
||||
Ta string `db:"tamil" json:"ta"`
|
||||
Te string `db:"telugu" json:"te"`
|
||||
Ml string `db:"malayalam" json:"ml"`
|
||||
Kn string `db:"kannada" json:"kn"`
|
||||
Gu string `db:"gujarati" json:"gu"`
|
||||
Ml string `db:"malayalam" json:"ml"`
|
||||
Or string `db:"oriya" json:"or"`
|
||||
Gu string `db:"gujarati" json:"gu"`
|
||||
Ur string `db:"urdu" json:"ur"`
|
||||
Lus string `db:"mizo" json:"lus"`
|
||||
As string `db:"assamese" json:"as"`
|
||||
Pa string `db:"punjabi" json:"pa"`
|
||||
Mai string `db:"maithili" json:"mai"`
|
||||
Mwr string `db:"marwari" json:"mwr"`
|
||||
Sat string `db:"santali" json:"sat"`
|
||||
Ne string `db:"nepali" json:"ne"`
|
||||
Gom string `db:"konkani" json:"gom"`
|
||||
Tcy string `db:"tulu" json:"tcy"`
|
||||
@@ -51,17 +52,15 @@ var lang_codes []string = []string{
|
||||
"mr", // Marathi
|
||||
"ta", // Tamil
|
||||
"te", // Telugu
|
||||
"ml", // Malayalam
|
||||
"kn", // Kannada
|
||||
"gu", // Gujarati
|
||||
"ml", // Malayalam
|
||||
"or", // Oriya
|
||||
"gu", // Gujarati
|
||||
"ur", // Urdu
|
||||
"lus", // Mizo
|
||||
"as", // Assamese
|
||||
"pa", // Punjabi
|
||||
"mai", // Maithili
|
||||
"mwr", // Marwari
|
||||
"sat", // Santali
|
||||
"ne", // Nepali
|
||||
"gom", // Konkani
|
||||
"tcy", // Tulu
|
||||
@@ -74,6 +73,12 @@ var lang_codes []string = []string{
|
||||
|
||||
var db *sqlx.DB
|
||||
|
||||
func cleanup() {
|
||||
db.Close()
|
||||
log.Printf("Shutting down...\n")
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the cached translation from the database, for the given english text. The first parameter
|
||||
|
||||
@@ -99,7 +104,7 @@ func getCachedTranslation(data string) (bool, translationStruct) {
|
||||
}
|
||||
|
||||
func addToDatabase(translation translationStruct) {
|
||||
_, err := db.NamedExec(`INSERT INTO translations VALUES (:english, :hindi, :bengali, :marathi, :tamil, :telugu, :kannada, :malayalam, :oriya, :gujarati, :marwari, :urdu, :mizo, :assamese, :punjabi, :maithili, :santali, :nepali, :konkani, :tulu, :bhojpuri, :dogri, :manipuri, :sindhi, :awadhi)`, &translation)
|
||||
_, err := db.NamedExec(`INSERT INTO translations VALUES (:english, :hindi, :bengali, :marathi, :tamil, :telugu, :kannada, :malayalam, :oriya, :gujarati, :urdu, :mizo, :assamese, :punjabi, :maithili, :nepali, :konkani, :tulu, :bhojpuri, :dogri, :manipuri, :sindhi, :awadhi)`, &translation)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -157,10 +162,10 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
langToTranslation[lang_code] = translation
|
||||
}
|
||||
langToTranslation["en"] = toTranslate
|
||||
langToTranslationJson, _ := json.Marshal(langToTranslation)
|
||||
translation := translationStruct{}
|
||||
err := json.Unmarshal(langToTranslationJson, &translation)
|
||||
translation.En = toTranslate // langToTranslation doesn't contain the english value
|
||||
addToDatabase(translation)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -170,13 +175,28 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Starting server...")
|
||||
log.Printf("Starting server...")
|
||||
var err error
|
||||
db, err = sqlx.Connect("sqlite3", "../translations.db")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
// Catch signal
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs)
|
||||
go func() {
|
||||
for sig := range sigs {
|
||||
log.Printf("Received signal: %s", sig)
|
||||
switch sig {
|
||||
case syscall.SIGURG:
|
||||
log.Printf("Ignoring sigurg")
|
||||
case syscall.SIGTERM, syscall.SIGINT:
|
||||
cleanup()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
http.HandleFunc("/", handler)
|
||||
log.Fatal(http.ListenAndServe(":9090", nil))
|
||||
|
||||
7
how_to_update_language_boundaries.txt
Normal file
7
how_to_update_language_boundaries.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
(Very jank)
|
||||
1. Make changes in the code eg. changing the language of a state or district.
|
||||
2. Uncomment the snippet of code at the bottom of index.js. This is responsible for printing out the language boundaries as JSON.
|
||||
3. Get the JSON from the browser console, put it in a file, and un-minifiy it.
|
||||
4. Copy the resulting JSON's text into 'india_with_districts_with_languages.json'. Put it at the start of the file, replacing the old language boundaries.
|
||||
5. Fix any remaining errors that pop up (should be nothing major).
|
||||
a. If Tulu and Sindhi are not loading, it's because, for some reason, the JSON definition for these languages includes the district that they're spoken in (because they're only spoken in 1 district). So the code assumes that it's a _district_, rather than a _language_ definition.
|
||||
1
htmx.min.js
vendored
Normal file
1
htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
162
index.html
162
index.html
@@ -4,13 +4,13 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
|
||||
|
||||
<title>Indian Translate</title>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js" charset="utf-8"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
|
||||
<script src="./htmx.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #f4f4f4;
|
||||
@@ -19,45 +19,70 @@
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-evenly;
|
||||
gap: 2em 2em;
|
||||
}
|
||||
|
||||
svg {
|
||||
border: 1px solid;
|
||||
padding: 2em;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 1s, visibility 2s;
|
||||
}
|
||||
|
||||
svg.show {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-block: 0.67em;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.state {
|
||||
stroke: black;
|
||||
fill: none;
|
||||
stroke-width: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
.language {
|
||||
stroke: red;
|
||||
/* There has to be a fill, even if it's transparent, to allow
|
||||
hover events to be recognized on the inside. */
|
||||
fill: black;
|
||||
fill-opacity: 0.0;
|
||||
/* fill: black;
|
||||
fill-opacity: 0.0; */
|
||||
fill-opacity: 0.8;
|
||||
stroke-width: 1;
|
||||
}
|
||||
.languageText, .romanizationText {
|
||||
.translationText, .romanizationText {
|
||||
visibility: hidden;
|
||||
font-family: "Noto Sans";
|
||||
font-size:1.25em;
|
||||
pointer-events: none;
|
||||
font-size:1.2em;
|
||||
}
|
||||
|
||||
.language:hover ~ .languageText {
|
||||
visibility: visible;
|
||||
}
|
||||
.testClass:hover {
|
||||
fill: red;
|
||||
cursor: default;
|
||||
}
|
||||
.romanizationText {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.languageText {
|
||||
pointer-events: none;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
font-family: sans-serif;
|
||||
visibility: hidden;
|
||||
}
|
||||
.language:hover ~ .languageText {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.district {
|
||||
stroke: white;
|
||||
stroke-width: 0.25;
|
||||
transition: fill 0.3s;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.language:hover {
|
||||
stroke-width: 2;
|
||||
}
|
||||
@@ -75,21 +100,122 @@
|
||||
.loading, .htmx-request.loading-indicator /* While request is being made */ {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Dim all other states */
|
||||
/* Kinda wild that you can do this in plain CSS */
|
||||
#indiaMap:has(.language:hover) .language:not(:hover) {
|
||||
fill-opacity: 0.5;
|
||||
transition: fill-opacity 0.3s;
|
||||
}
|
||||
|
||||
#indiaMap .language:hover {
|
||||
fill-opacity: 1;
|
||||
transition: fill-opacity 0.3s;
|
||||
}
|
||||
|
||||
/* Position map load spinner in the middle of the SVG */
|
||||
#svgContainer {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#svgContainer .mapLoadSpinner {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 40%;
|
||||
}
|
||||
|
||||
/* Credit to https://lukehaas.me/projects/css-loaders/ */
|
||||
.mapLoadSpinner,
|
||||
.mapLoadSpinner:after {
|
||||
border-radius: 50%;
|
||||
width: 10em;
|
||||
height: 10em;
|
||||
transition: opacity 1s, visibility 2s;
|
||||
}
|
||||
.mapLoadSpinner.hide {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.mapLoadSpinner {
|
||||
margin: 60px auto;
|
||||
font-size: 10px;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
border-top: 1.1em solid rgba(255,158,83, 0.2);
|
||||
border-right: 1.1em solid rgba(255,158,83, 0.2);
|
||||
border-bottom: 1.1em solid rgba(255,158,83, 0.2);
|
||||
border-left: 1.1em solid #ff9e53;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-animation: load8 1.1s infinite linear;
|
||||
animation: load8 1.1s infinite linear;
|
||||
}
|
||||
@-webkit-keyframes load8 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes load8 {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#fetchingText {
|
||||
visibility: hidden;
|
||||
}
|
||||
@media only screen and (max-width: 768px) {
|
||||
main {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
.district {
|
||||
stroke-width: 0.1;
|
||||
}
|
||||
.translationText, .romanizationText {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
.languageText {
|
||||
font-size: 0.6em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section id="textStuff">
|
||||
<h1>Indian Translate</h1>
|
||||
<form hx-get="/submit" hx-swap=none hx-indicator="#loading-screen" hx-on::after-request="updateTranslations(event.detail.xhr.response)" class="translateForm" method="get">
|
||||
<form hx-get="/submit" hx-swap=none hx-indicator="#loading-screen" hx-on::before-request="hideTranslationsAndShowText(event.detail.xhr.response)" hx-on::after-request="updateTranslations(event.detail.xhr.response)" class="translateForm" method="get">
|
||||
<label for="query">Enter text to translate:</label>
|
||||
<input type="text" name="query" id="query" required/>
|
||||
<input type="submit" value="Translate"/>
|
||||
</form>
|
||||
<progress id="loading-screen" class="loading-indicator"></progress>
|
||||
<h3 id="fetchingText">Fetching translations...</h3>
|
||||
<noscript>
|
||||
<h2>This website requires javascript to work.</h2>
|
||||
</noscript>
|
||||
</section>
|
||||
|
||||
<svg id = "indiaMap" width="1000" height="1000"></svg>
|
||||
<section id="svgContainer">
|
||||
<div id="mapLoadSpinner" class="mapLoadSpinner"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
|
||||
243
index.js
243
index.js
@@ -1,5 +1,3 @@
|
||||
const svg = d3.select("svg")
|
||||
|
||||
const defaultColor = "#555555"
|
||||
|
||||
const languages = {
|
||||
@@ -11,7 +9,6 @@ const languages = {
|
||||
konkani: {name: "Konkani", color: "#9b7cff", code: "gom", districts: []},
|
||||
hindi: {name: "Hindi", color: "#d17cff", code: "hi", districts: []},
|
||||
gujarati: {name: "Gujarati", color: "#7cffee", code: "gu", districts: []},
|
||||
marwari: {name: "Marwari", color: "#7bc4c9", code: "mwr", districts: []},
|
||||
oriya: {name: "Oriya", color: "#9bcc9f", code: "or", districts: []},
|
||||
bengali: {name: "Bengali", color: "#bf9a77", code: "bn", districts: []},
|
||||
punjabi: {name: "Punjabi", color: "#e84a35", code: "pa", districts: []},
|
||||
@@ -24,7 +21,6 @@ const languages = {
|
||||
urdu: {name: "Urdu", color: "#3fa179", code: "ur", districts: []},
|
||||
tulu: {name: "Tulu", color: "#dedc52", code: "tcy", districts: []},
|
||||
maithali: {name: "Maithali", color: "#4472a6", code: "mai", districts: []},
|
||||
santali: {name: "Santali", color: "#96bf60", code: "sat", districts: []},
|
||||
sindhi: {name: "Sindhi", color: "#e89931", code: "sd", districts: []},
|
||||
awadhi: {name: "Awadhi", color: "#847fb5", code: "awa", districts: []},
|
||||
};
|
||||
@@ -40,7 +36,7 @@ const state2lang = {
|
||||
"Goa": languages["konkani"],
|
||||
"Odisha": languages["oriya"],
|
||||
"Gujarat": languages["gujarati"],
|
||||
"Rajasthan": languages["marwari"],
|
||||
"Rajasthan": languages["hindi"],
|
||||
"Chhattisgarh": languages["hindi"],
|
||||
"Jharkhand": languages["hindi"], // DEFAULT
|
||||
"West Bengal": languages["bengali"],
|
||||
@@ -118,17 +114,6 @@ const district2lang = { // Should override state colors
|
||||
|
||||
"Kutch": languages["sindhi"],
|
||||
|
||||
"Godda": languages["santali"],
|
||||
"Deoghar": languages["santali"],
|
||||
"Dumka": languages["santali"],
|
||||
"Jamtara": languages["santali"],
|
||||
"Sahibganj": languages["santali"],
|
||||
"Pakur": languages["santali"],
|
||||
"East Singhbhum": languages["santali"],
|
||||
"Jhargram": languages["santali"],
|
||||
"Bankura": languages["santali"],
|
||||
"Purulia": languages["santali"],
|
||||
|
||||
"Kanpur": languages["awadhi"],
|
||||
"Lakhimpur Kheri": languages["awadhi"],
|
||||
"Sitapur": languages["awadhi"],
|
||||
@@ -142,6 +127,33 @@ const district2lang = { // Should override state colors
|
||||
"Bahraich": languages["awadhi"],
|
||||
}
|
||||
|
||||
function responsivefy(svg) {
|
||||
// get container + svg aspect ratio
|
||||
var container = d3.select(svg.node().parentNode),
|
||||
width = parseInt(svg.style("width")),
|
||||
height = parseInt(svg.style("height")),
|
||||
aspect = width / height;
|
||||
|
||||
// add viewBox and preserveAspectRatio properties,
|
||||
// and call resize so that svg resizes on inital page load
|
||||
svg.attr("viewBox", "0 0 " + width + " " + height)
|
||||
.attr("perserveAspectRatio", "xMinYMid")
|
||||
.call(resize);
|
||||
|
||||
// to register multiple listeners for same event type,
|
||||
// you need to add namespace, i.e., 'click.foo'
|
||||
// necessary if you call invoke this function for multiple svgs
|
||||
// api docs: https://github.com/mbostock/d3/wiki/Selections#on
|
||||
d3.select(window).on("resize." + container.attr("id"), resize);
|
||||
|
||||
// get width of container and resize svg to fit it
|
||||
function resize() {
|
||||
var targetWidth = Math.floor(container.node().getBoundingClientRect().width);
|
||||
svg.attr("width", targetWidth);
|
||||
svg.attr("height", Math.round(targetWidth / aspect));
|
||||
}
|
||||
}
|
||||
|
||||
// Functions for calculating and dealing with language boundaries
|
||||
function reverseCoordArrays(coords) {
|
||||
if (!Array.isArray(coords)) {
|
||||
@@ -198,12 +210,40 @@ function stateOrDistrictOrLanguage(d) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// const mapWidth = document.getElementById("indiaMap").getAttribute("width")
|
||||
// const mapHeight = document.getElementById("indiaMap").getAttribute("height")
|
||||
const mapWidth = /*window.innerWidth - */document.querySelector("#svgContainer").offsetWidth * 0.85;
|
||||
// const mapHeight = document.querySelector("#svgContainer").offsetHeight;
|
||||
const mapHeight = (window.innerHeight - document.querySelector("#svgContainer").getBoundingClientRect().top);
|
||||
|
||||
const svg = d3.select("#svgContainer")
|
||||
.append('svg')
|
||||
.attr('width', mapWidth.toString())
|
||||
.attr('height', (mapHeight).toString())
|
||||
// .attr('viewbox', '0 0 ' + mapWidth.toString() + ' ' + mapHeight.toString())
|
||||
// .attr('preserveAspectRatio', "xMidYMin")
|
||||
.attr('id', 'indiaMap')
|
||||
// .call(responsivefy);
|
||||
|
||||
function drawMap(world) {
|
||||
const mapWidth = document.getElementById("indiaMap").getAttribute("width")
|
||||
const mapHeight = document.getElementById("indiaMap").getAttribute("height")
|
||||
const projection = d3.geoMercator().fitSize([mapWidth, mapHeight], world)
|
||||
const path = d3.geoPath().projection(projection);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const newSvg = d3.select('svg');
|
||||
const bbox = newSvg.node().getBBox();
|
||||
const originalWidth = +newSvg.attr("width");
|
||||
newSvg
|
||||
// .attr("viewBox", `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`)
|
||||
.attr("viewBox", `0 ${bbox.y} ${originalWidth} ${bbox.height}`)
|
||||
// .attr("width", mapHeight.toString())
|
||||
.attr("height", bbox.height)
|
||||
// .style("display", "block")
|
||||
// .style("max-height", "100%"); // optional: keep it scalable in a flexbox
|
||||
});
|
||||
|
||||
|
||||
const states = svg.selectAll("g")
|
||||
.data(world.features)
|
||||
.enter()
|
||||
@@ -213,13 +253,8 @@ function drawMap(world) {
|
||||
.attr("d", path)
|
||||
.attr("class", d => stateOrDistrictOrLanguage(d))
|
||||
.attr("fill", function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) === "district") {
|
||||
const districtLang = district2langFunc(d)
|
||||
if (typeof districtLang !== 'undefined') {
|
||||
return districtLang.color
|
||||
} else {
|
||||
return defaultColor;
|
||||
}
|
||||
if (stateOrDistrictOrLanguage(d) === "language") {
|
||||
return languages[d.properties.lang_name.toLowerCase()].color;
|
||||
}
|
||||
})
|
||||
.each(function(d) {
|
||||
@@ -229,17 +264,64 @@ function drawMap(world) {
|
||||
districtLang.districts.push(d)
|
||||
}
|
||||
}
|
||||
// Hide map load spinner after map has loaded
|
||||
document.getElementById("mapLoadSpinner").classList.add("hide");
|
||||
document.querySelector("svg").classList.add("show");
|
||||
|
||||
})
|
||||
.append("title") // Tooltip
|
||||
.text(d => d.properties.district);
|
||||
|
||||
states.append("text")
|
||||
.attr("x", d => projection(d3.geoCentroid(d))[0])
|
||||
.attr("y", d => projection(d3.geoCentroid(d))[1])
|
||||
.attr("x", function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||
rtv = projection(d3.geoCentroid(d))[0];
|
||||
if (d.properties.lang_name == "Kannada") {
|
||||
rtv -= 20;
|
||||
}
|
||||
if (d.properties.lang_name == "Tamil") {
|
||||
rtv += 20;
|
||||
}
|
||||
if (d.properties.lang_name == "Maithali") {
|
||||
rtv += 10;
|
||||
}
|
||||
if (d.properties.lang_name == "Konkani") {
|
||||
rtv -= 15;
|
||||
}
|
||||
if (d.properties.lang_name == "Bengali") {
|
||||
rtv -= 15;
|
||||
}
|
||||
return rtv
|
||||
}
|
||||
})
|
||||
.attr("y", function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||
rtv = projection(d3.geoCentroid(d))[1]
|
||||
if (d.properties.lang_name == "Kannada") {
|
||||
rtv += 15;
|
||||
}
|
||||
if (d.properties.lang_name == "Tamil") {
|
||||
rtv -= 20;
|
||||
}
|
||||
if (d.properties.lang_name == "Gujarati") {
|
||||
rtv -= 10;
|
||||
}
|
||||
if (d.properties.lang_name == "Mizo") {
|
||||
rtv += 20;
|
||||
}
|
||||
if (d.properties.lang_name == "Nepali") {
|
||||
rtv -= 10;
|
||||
}
|
||||
if (d.properties.lang_name == "Bengali") {
|
||||
rtv += 25;
|
||||
}
|
||||
|
||||
return rtv
|
||||
}
|
||||
})
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", "12px")
|
||||
.attr("fill", "black")
|
||||
.attr("class", "languageText")
|
||||
.attr("class", "translationText")
|
||||
.attr("id", function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||
return d.properties.lang_code+"Text"
|
||||
@@ -255,11 +337,15 @@ function drawMap(world) {
|
||||
}
|
||||
});
|
||||
|
||||
// Romanization
|
||||
states.append("text")
|
||||
.attr("x", d => projection(d3.geoCentroid(d))[0])
|
||||
.attr("y", d => projection(d3.geoCentroid(d))[1] + parseFloat(getComputedStyle(document.getElementsByClassName('languageText')[0]).getPropertyValue('font-size')))
|
||||
.attr("x", d => stateOrDistrictOrLanguage(d) == "language" ?
|
||||
document.getElementById(d.properties.lang_code + "Text").getAttribute("x") :
|
||||
projection(d3.geoCentroid(d))[0])
|
||||
.attr("y", d => stateOrDistrictOrLanguage(d) == "language" ?
|
||||
parseFloat(document.getElementById(d.properties.lang_code + "Text").getAttribute("y")) + parseFloat(getComputedStyle(document.getElementsByClassName('translationText')[0]).getPropertyValue('font-size')) :
|
||||
projection(d3.geoCentroid(d))[1])
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", "12px")
|
||||
.attr("fill", "black")
|
||||
.attr("class", "romanizationText")
|
||||
.attr("id", function(d) {
|
||||
@@ -269,48 +355,77 @@ function drawMap(world) {
|
||||
d3.select(this).remove()
|
||||
}
|
||||
})
|
||||
.text(function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||
return "(" + d.properties.lang_name + ")";
|
||||
} else {
|
||||
.each(function(d) {
|
||||
if (!stateOrDistrictOrLanguage(d) == "language") {
|
||||
d3.select(this).remove() // Only add this attribute if the element is a language
|
||||
}
|
||||
});
|
||||
|
||||
// Language
|
||||
states.append("text")
|
||||
.attr("x", d => stateOrDistrictOrLanguage(d) == "language" ?
|
||||
document.getElementById(d.properties.lang_code + "Text").getAttribute("x") :
|
||||
projection(d3.geoCentroid(d))[0])
|
||||
.attr("y", d => stateOrDistrictOrLanguage(d) == "language" ?
|
||||
parseFloat(document.getElementById(d.properties.lang_code + "Text").getAttribute("y")) - parseFloat(getComputedStyle(document.getElementsByClassName('translationText')[0]).getPropertyValue('font-size')) :
|
||||
projection(d3.geoCentroid(d))[1])
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("fill", "black")
|
||||
.attr("class", "languageText")
|
||||
.attr("id", function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||
return d.properties.lang_code+"Language"
|
||||
} else {
|
||||
d3.select(this).remove()
|
||||
}
|
||||
})
|
||||
.each(function(d) {
|
||||
if (!stateOrDistrictOrLanguage(d) == "language") {
|
||||
d3.select(this).remove() // Only add this attribute if the element is a language
|
||||
}
|
||||
})
|
||||
.text(function(d) {
|
||||
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||
return d.properties.lang_name;
|
||||
} else {
|
||||
d3.select(this).remove() // Only add this attribute if the element is a language
|
||||
}
|
||||
})
|
||||
let allLangs = []
|
||||
|
||||
const coordinates = [77.69916967457782,23.389970772934166];
|
||||
const [xCoord, yCoord] = projection(coordinates);
|
||||
|
||||
svg.append("text")
|
||||
.attr("x", xCoord)
|
||||
.attr("y", yCoord)
|
||||
.attr("class", "testClass")
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", "12px")
|
||||
.attr("fill", "black")
|
||||
.text("Hello, Map!");
|
||||
// for (const [langId,lang] of Object.entries(languages)) {
|
||||
// let geojson = {
|
||||
// "type": "FeatureCollection",
|
||||
// "features": lang.districts
|
||||
// };
|
||||
//
|
||||
// let outerBound = getOuterBoundaryPolygon(geojson.features)
|
||||
// outerBound["id"] = "lang" + lang.name
|
||||
// outerBound.properties["lang_name"]= lang.name
|
||||
// outerBound.properties["lang_code"]= lang.code
|
||||
// allLangs.push(outerBound);
|
||||
// svg.append("text")
|
||||
// .attr("x", xCoord)
|
||||
// .attr("y", yCoord)
|
||||
// .attr("class", "testClass")
|
||||
// .attr("text-anchor", "middle")
|
||||
// .attr("font-size", "12px")
|
||||
// .attr("fill", "black")
|
||||
// .text("Hello, Map!");
|
||||
|
||||
// svg.append("path")
|
||||
// .datum(outerBound)
|
||||
// .attr("d", path)
|
||||
// .attr("fill", "none")
|
||||
// .attr("stroke", "red")
|
||||
// .attr("stroke-width", 2)
|
||||
// }
|
||||
// console.log(JSON.stringify(allLangs))
|
||||
// for (const [langId,lang] of Object.entries(languages)) {
|
||||
// let geojson = {
|
||||
// "type": "FeatureCollection",
|
||||
// "features": lang.districts
|
||||
// };
|
||||
//
|
||||
// let outerBound = getOuterBoundaryPolygon(geojson.features)
|
||||
// outerBound["id"] = "lang" + lang.name
|
||||
// outerBound.properties["lang_name"]= lang.name
|
||||
// outerBound.properties["lang_code"]= lang.code
|
||||
// allLangs.push(outerBound);
|
||||
//
|
||||
// svg.append("path")
|
||||
// .datum(outerBound)
|
||||
// .attr("d", path)
|
||||
// .attr("fill", "none")
|
||||
// .attr("stroke", "red")
|
||||
// .attr("stroke-width", 2)
|
||||
// }
|
||||
// console.log(JSON.stringify(allLangs))
|
||||
|
||||
}
|
||||
|
||||
d3.json("india_with_districts_with_languages.json").then(drawMap)
|
||||
d3.json("india_with_districts_with_languages_min.json").then(drawMap)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1
india_with_districts_with_languages_min.json
Normal file
1
india_with_districts_with_languages_min.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,6 @@ Mizo
|
||||
Assamese
|
||||
Punjabi
|
||||
Maithili
|
||||
Santali
|
||||
Nepali
|
||||
Konkani
|
||||
Tulu
|
||||
|
||||
3
server.py
Normal file
3
server.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from ai4bharat.transliteration import xlit_server
|
||||
app, engine = xlit_server.get_app()
|
||||
app.run(host='0.0.0.0', port=10000)
|
||||
7
todo.txt
7
todo.txt
@@ -1,6 +1,7 @@
|
||||
1. REFACTOR
|
||||
2. Show language names as well, along with translation and romanization
|
||||
3. Figure out positioning of the different translations, show all of them instead of having to hover
|
||||
3. Figure out positioning of the different translations, show all of them instead of having to hover; for smaller languages it's hard to move the mouse around to see the full word.
|
||||
4. Better translations (some of them are just terrible with Google Translate - use the ai4bharat one instead?)
|
||||
4. Cache romanizations in DB
|
||||
5. General beautification
|
||||
6. TTS?
|
||||
6. General beautification
|
||||
7. TTS?
|
||||
|
||||
BIN
translations.db
BIN
translations.db
Binary file not shown.
@@ -1,6 +1,11 @@
|
||||
function hideTranslationsAndShowText(request) {
|
||||
document.querySelectorAll(".translationText, .romanizationText").forEach(element => element.style.visibility = 'hidden')
|
||||
document.getElementById("fetchingText").style.visibility = "visible";
|
||||
}
|
||||
|
||||
function updateTranslations(response) {
|
||||
const translations = JSON.parse(response);
|
||||
document.querySelectorAll(".languageText").forEach(element => {
|
||||
document.querySelectorAll(".translationText").forEach(element => {
|
||||
element.textContent = translations[element.id.replace("Text", "")];
|
||||
});
|
||||
|
||||
@@ -10,6 +15,7 @@ function updateTranslations(response) {
|
||||
// bar used to track translations, and then remove it in the 'resolved' handler.
|
||||
const elem = document.getElementById("loading-screen")
|
||||
elem.classList.toggle("loading")
|
||||
document.getElementById("fetchingText").textContent = "Fetching romanizations..."
|
||||
fetch("/romanize", {
|
||||
method: "POST",
|
||||
body: response
|
||||
@@ -23,5 +29,9 @@ function updateTranslations(response) {
|
||||
element.textContent = "(" + romanizations[element.id.replace("Romanization", "")] + ")";
|
||||
}
|
||||
});
|
||||
// Show elements again
|
||||
document.querySelectorAll(".translationText, .romanizationText").forEach(element => element.style.visibility = 'visible')
|
||||
document.getElementById("fetchingText").textContent = "Fetching translations..." // Restore the original text content
|
||||
document.getElementById("fetchingText").style.visibility = "hidden"
|
||||
});
|
||||
}
|
||||
|
||||
138
xlit_server.py
138
xlit_server.py
@@ -195,57 +195,93 @@ def ulca_api():
|
||||
|
||||
@app.route('/romanize', methods=['POST'])
|
||||
def romanizeHandler():
|
||||
langCodeLookup = {
|
||||
"as": "as",
|
||||
"bn": "bn",
|
||||
"gom": "gom",
|
||||
"gu": "gu",
|
||||
"hi": "hi",
|
||||
"kn": "kn",
|
||||
"mai": "mai",
|
||||
"ml": "ml",
|
||||
"mni-Mtei": "mni",
|
||||
"mr": "mr",
|
||||
"ne": "ne",
|
||||
"or": "or",
|
||||
"pa": "pa",
|
||||
"sd": "sd",
|
||||
"ta": "ta",
|
||||
"te": "te",
|
||||
"ur": "ur"
|
||||
}
|
||||
rtv = dict()
|
||||
langCodeLookup = {
|
||||
"hi": "hi",
|
||||
"bn": "bn",
|
||||
"mr": "mr",
|
||||
"ta": "ta",
|
||||
"te": "te",
|
||||
"kn": "kn",
|
||||
"ml": "ml",
|
||||
"or": "or",
|
||||
"gu": "gu",
|
||||
"ur": "ur",
|
||||
"as": "as",
|
||||
"pa": "pa",
|
||||
"mai": "mai",
|
||||
"ne": "ne",
|
||||
"gom": "gom",
|
||||
"tcy": "kn", # Tulu uses Kannada script
|
||||
"bho": "hi", # Bhojpuri uses Hindi script
|
||||
"doi": "hi", # Dogri uses Hindi script
|
||||
"mni-Mtei": "mni",
|
||||
"sd": "sd",
|
||||
"awa": "hi", # Awadhi uses Hindi script
|
||||
}
|
||||
|
||||
data = request.get_json(force=True)
|
||||
lang2code = {
|
||||
"hindi": "hi",
|
||||
"bengali": "bn",
|
||||
"marathi": "mr",
|
||||
"tamil": "ta",
|
||||
"telugu": "te",
|
||||
"malayalam": "ml",
|
||||
"kannada": "kn",
|
||||
"oriya": "or",
|
||||
"gujarati": "gu",
|
||||
"urdu": "ur",
|
||||
"assamese": "as",
|
||||
"punjabi": "pa",
|
||||
"maithili": "mai",
|
||||
"nepali": "ne",
|
||||
"konkani": "gom",
|
||||
"tulu": "tcy",
|
||||
"bhojpuri": "bho",
|
||||
"dogri": "doi",
|
||||
"manipuri": "mni-Mtei",
|
||||
"sindhi": "sd",
|
||||
"awadhi": "awa",
|
||||
"english": "en",
|
||||
}
|
||||
code2lang = {v:k for k,v in lang2code.items()}
|
||||
|
||||
# Check if database contains the romanizations already
|
||||
englishWord = data['en']
|
||||
print(englishWord)
|
||||
con = sqlite3.connect("../translations.db")
|
||||
cur = con.cursor()
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS romanizations AS SELECT * FROM translations WHERE 0") # Copy schema from 'translations' table
|
||||
cur.execute('SELECT * FROM romanizations WHERE english = ?', (englishWord,))
|
||||
romanizations = cur.fetchall()
|
||||
columnNames = [column[0] for column in cur.description]
|
||||
romanizationsDict = []
|
||||
if len(romanizations) > 0:
|
||||
for row in romanizations:
|
||||
row_dict = {columnNames[i]: row[i] for i in range(len(columns))}
|
||||
romanizationsDict.append(row_dict)
|
||||
json_data = json.dumps(romanizationsdata, indent=4)
|
||||
print(json_data)
|
||||
# if len(romanizations) != 0:
|
||||
|
||||
# Assuming the romanizations didn't exist before
|
||||
for key in data:
|
||||
if key in langCodeLookup:
|
||||
langCode = langCodeLookup[key]
|
||||
text = data[key]
|
||||
response = reverse_xlit_api(langCode, text)
|
||||
responseJson = response.get_json()
|
||||
rtv[key] = responseJson['result']
|
||||
|
||||
rtvJson = jsonify(rtv)
|
||||
rtv = dict()
|
||||
|
||||
con.close()
|
||||
return rtvJson
|
||||
data = request.get_json(force=True)
|
||||
|
||||
# Check if database contains the romanizations already
|
||||
englishWord = data['en']
|
||||
rtv["en"] = englishWord
|
||||
print(englishWord)
|
||||
con = sqlite3.connect("../translations.db")
|
||||
cur = con.cursor()
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS romanizations AS SELECT * FROM translations WHERE 0") # Copy schema from 'translations' table
|
||||
cur.execute('SELECT * FROM romanizations WHERE english = ?', (englishWord,))
|
||||
romanizations = cur.fetchall()
|
||||
columnNames = [column[0] for column in cur.description]
|
||||
romanizationsDict = []
|
||||
if len(romanizations) > 0:
|
||||
for row in romanizations:
|
||||
row_dict = {lang2code[columnNames[i]]: row[i] for i in range(len(langCodeLookup)+1)} # The '+1' is because of English, which isn't in langCodeLookup
|
||||
romanizationsDict.append(row_dict)
|
||||
json_data = jsonify(romanizationsDict[0])
|
||||
con.close()
|
||||
return json_data
|
||||
# if len(romanizations) != 0:
|
||||
|
||||
# Assuming the romanizations didn't exist before
|
||||
for key in data:
|
||||
if key in langCodeLookup:
|
||||
langCode = langCodeLookup[key]
|
||||
text = data[key]
|
||||
response = reverse_xlit_api(langCode, text)
|
||||
responseJson = response.get_json()
|
||||
rtv[key] = responseJson['result']
|
||||
|
||||
rtvJson = jsonify(rtv)
|
||||
rtv["en"] = englishWord
|
||||
cur.execute("INSERT INTO romanizations " + str(tuple([code2lang[val] for val in rtv.keys()])) + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", tuple(rtv.values()))
|
||||
con.commit()
|
||||
|
||||
con.close()
|
||||
return rtvJson
|
||||
|
||||
Reference in New Issue
Block a user