Compare commits

...

54 Commits

Author SHA1 Message Date
2949c48ed9 Merge branch 'master' into resizeSvg 2025-04-12 10:57:55 -05:00
6aedbbc942 Styling changes mostly done 2025-04-12 11:55:40 -04:00
977cd1aa62 Add parentheses around arithmetic operation 2025-04-11 10:17:56 -04:00
81fe778212 Works even better now 2025-04-11 10:12:57 -04:00
bf8825294b Mostly working 2025-04-10 11:37:55 -04:00
6e9e11c41c Made some progress 2025-04-09 10:34:52 -04:00
66d88c133b Started working on resizing SVG 2025-04-08 23:53:13 -04:00
f62697d03a Kept changes from production use 2025-04-06 11:24:56 -05:00
ad254d2760 Added separate styling for mobile 2025-04-06 12:01:20 -04:00
49b4820c2d Removed Santali language (since it doesnt' have transliteration support) 2025-04-06 12:01:04 -04:00
3d0c20ae48 Added signal handling 2025-03-29 11:59:36 -05:00
3d06971e77 Remove testing code; use minified map JSON; hide spinner after map has loaded 2025-03-28 14:41:51 -05:00
50a476edad Change function name; add code to hide, show and change text for the 'fetching translations/romanizations' text 2025-03-28 14:41:02 -05:00
2814f80fc8 Update translations DB 2025-03-28 14:40:39 -05:00
74f3cb9740 Added minified version of JSON 2025-03-28 14:40:26 -05:00
2f7cdd2ea7 Add loading spinner while map loads; fade in map and fade out spinner; load HTMX locally; show text while translations and romanizations are being fetched 2025-03-28 14:40:10 -05:00
1fbe5a1abf Download HTMX locally 2025-03-28 13:46:31 -05:00
c15bd02c17 Added pointer-events: none for state boundaries, updated translations 2025-03-28 10:02:49 -05:00
bf453f9934 Added romanization server code 2025-03-27 08:47:36 -04:00
a4a2451edd Updated translations DB 2025-03-25 11:50:21 -04:00
56cae83339 Move Tulu language name a little 2025-03-18 15:57:56 -04:00
53cd9b9465 More styling - increase language font size 2025-03-18 15:57:34 -04:00
fe2aaef413 Updated DB 2025-03-18 15:36:17 -04:00
f26a5b8c59 Fix ordering of languages when inserting into DB 2025-03-18 15:36:02 -04:00
2d810bb661 Updated references to classname 2025-03-18 15:34:51 -04:00
cf5b2e40a0 Adjusted positioning of some language translations; added new text element for the language name 2025-03-18 15:34:25 -04:00
0c11a302e3 Added styling for language text 2025-03-18 15:33:34 -04:00
9cc650a467 Fix ordering of languages when inserting into DB 2025-03-18 15:32:23 -04:00
0e26b2345a Show all texts at once; hide them while the results are being fetched; show them after the results have been fetched 2025-03-16 22:15:25 -04:00
38316b0ea9 Cleared romanizations, because of an out-of-order thing 2025-03-16 22:14:52 -04:00
b2055b11d9 Fixed issues with fetching romanizations from DB 2025-03-12 16:06:52 -04:00
f0395763e5 Updated translations DB 2025-03-12 16:06:23 -04:00
776e45686e Updated DB 2025-03-10 06:33:36 -04:00
ab4acd189d Started working on adding romanizations to DB 2025-03-10 06:33:31 -04:00
93ff0d172e Added how-to file 2025-03-08 12:31:10 -05:00
f495d939cb Updated TODO 2025-03-08 12:30:57 -05:00
d8c2b22a46 Changed Rajasthan's language from Marwari to Hindi 2025-03-08 12:30:47 -05:00
f65ea1dd8f Added more transliterations (some languages use the same script as others) 2025-03-08 12:30:18 -05:00
da993bf1aa Removed marwari (Rajasthan mostly speaks Hindi) 2025-03-08 12:29:23 -05:00
7110b0e0e3 Set romanization text relative to translation text; Fill languages instead of the individual districts 2025-03-07 15:46:14 -05:00
d1feca7003 Make districts transparent; dim other languages when one is hovered; change default opacity of language 2025-03-07 15:45:27 -05:00
3aa4135a81 Put languages before districts, so that district boundaries show up even with full opacity 2025-03-07 15:44:39 -05:00
72747015ed Check if romanizations already exist in database; transliterate the whole sentence instead of just one word 2025-03-06 21:21:50 -05:00
d006ee8887 Update translations DB 2025-03-06 21:21:05 -05:00
6cc8069c23 Moved translations DB to root, updated references to it 2025-03-06 09:26:35 -05:00
0add8244e6 Added gitignore 2025-03-06 09:25:29 -05:00
02c47cc218 Updated TODO 2025-03-05 16:47:09 -05:00
f20c17337b Added styling for romanizations (same as translations) 2025-03-05 16:46:22 -05:00
4fece83e43 Show romanizations on map 2025-03-05 16:46:12 -05:00
f861b73846 Updated TODO 2025-03-05 14:45:40 -05:00
0f92b11009 Add more text below the translation, in brackets - will be used for romanization 2025-03-05 14:44:58 -05:00
6899c2ce86 Updated translation DB 2025-03-05 14:44:37 -05:00
5cb766c2b8 Updated DB 2025-03-05 07:07:47 -05:00
c5ff92f941 Added JS to fetch romanization from an endpoint and log it; added a CSS class to the loading bar to show while the data is being fetched 2025-03-05 07:07:40 -05:00
16 changed files with 16708 additions and 21694 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
ai4bharat-transliteration

View File

@@ -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")
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))

Binary file not shown.

View 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

File diff suppressed because one or more lines are too long

View File

@@ -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,44 +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 {
.translationText, .romanizationText {
visibility: hidden;
font-family: "Noto Sans";
font-size:1.25em;
pointer-events: none;
}
.language:hover ~ .languageText {
visibility: visible;
}
.testClass:hover {
fill: red;
cursor: default;
font-size:1.2em;
}
.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;
}
@@ -71,24 +97,125 @@
.loading-indicator {
display: none;
}
.htmx-request.loading-indicator /* While request is being made */ {
.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>

251
index.js
View File

@@ -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,40 +337,95 @@ function drawMap(world) {
}
});
// Romanization
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", "romanizationText")
.attr("id", function(d) {
if (stateOrDistrictOrLanguage(d) == "language") {
return d.properties.lang_code+"Romanization"
} 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
}
});
// 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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,6 @@ Mizo
Assamese
Punjabi
Maithili
Santali
Nepali
Konkani
Tulu

3
server.py Normal file
View 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)

View File

@@ -1,5 +1,7 @@
1. REFACTOR
3. Figure out positioning of the different translations, show all of them instead of having to hover
4. Show romanizations
5. General beautification
6. TTS?
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; 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
6. General beautification
7. TTS?

BIN
translations.db Normal file

Binary file not shown.

View File

@@ -1,6 +1,37 @@
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", "")];
});
// Send result to romanization sever
// Since this is an asynchronous opreation, there is no indication that
// it's being performed in the backend. So I add the 'loading' class to the progress
// 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
}).then((newResponse) => newResponse.json()) // It looks like response.json() returns another promise, since the _body_ of the response may not have loaded yet. Do not confuse this json() with the static Response.json() method. Since it returns another promise, I have to call .then() to actually get the result.
.then((data) => {
elem.classList.toggle("loading");
console.log(data);
const romanizations = data;
document.querySelectorAll(".romanizationText").forEach(element => {
if (element.id.replace("Romanization", "") in romanizations) {
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"
});
}

View File

@@ -21,6 +21,7 @@ from uuid import uuid4
from datetime import datetime
import traceback
import enum
import sqlite3
from .utils import LANG_CODE_TO_DISPLAY_NAME, RTL_LANG_CODES, LANG_CODE_TO_SCRIPT_CODE
@@ -127,7 +128,7 @@ def reverse_xlit_api(lang_code, word):
try:
## Limit char count to --> 70
xlit_result = ENGINE["indic2en"].translit_word(word[:70], lang_code, topk=num_suggestions)
xlit_result = ENGINE["indic2en"].translit_sentence(word, lang_code)
except Exception as e:
xlit_result = XlitError.internal_err
@@ -194,33 +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)
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']
return jsonify(rtv)
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()}
rtv = dict()
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