Compare commits
96 Commits
6532036c73
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| bef3c9b3d7 | |||
| 8f7d4e477f | |||
| 945d3d6526 | |||
| 485a3efd8f | |||
| 17878106d4 | |||
| 67fc0d4297 | |||
| 6716a4cd40 | |||
| 4e4570ed03 | |||
| 12d53370fb | |||
| 11b9635e50 | |||
| d769797136 | |||
| 4804497b3c | |||
| 80529e5882 | |||
| f6abc396b6 | |||
| 094d756a07 | |||
| fca0d3a5fa | |||
| 5efb1ad322 | |||
| 2e85a7c2b3 | |||
| 4658ec5a9d | |||
| 364a64f320 | |||
| 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 | |||
| 72747015ed | |||
| d006ee8887 | |||
| 6cc8069c23 | |||
| 0add8244e6 | |||
| 02c47cc218 | |||
| f20c17337b | |||
| 4fece83e43 | |||
| f861b73846 | |||
| 0f92b11009 | |||
| 6899c2ce86 | |||
| 5cb766c2b8 | |||
| c5ff92f941 | |||
| 4671ba40e7 | |||
| dfdd9326be | |||
| 64f4f5f80b | |||
| ca7f6ad212 | |||
| 7112874a06 | |||
| a38657ddf1 | |||
| 6ba1189c6c | |||
| d8bdb048c4 | |||
| a39fcc69a4 | |||
| 42e31dab13 | |||
| da3b3b8a16 | |||
| 0de268eec5 | |||
| 2b8bed7587 | |||
| 79944180d5 | |||
| 6cd33ac06d | |||
| e60da27cd5 | |||
| c57de2492e | |||
| 5f8264cda8 | |||
| 73149275fd | |||
| c7876fea3f | |||
| d757454319 | |||
| 53f8c63c2d |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ai4bharat-transliteration
|
||||||
41
golangServer/go.mod
Normal file
41
golangServer/go.mod
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
module example.com/m
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.116.0 // indirect
|
||||||
|
cloud.google.com/go/auth v0.13.0 // indirect
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||||
|
cloud.google.com/go/longrunning v0.6.2 // indirect
|
||||||
|
cloud.google.com/go/translate v1.12.3 // indirect
|
||||||
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/s2a-go v0.1.8 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
|
||||||
|
github.com/gorilla/css v1.0.1 // indirect
|
||||||
|
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||||
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
|
golang.org/x/net v0.33.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.24.0 // indirect
|
||||||
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
golang.org/x/time v0.8.0 // indirect
|
||||||
|
google.golang.org/api v0.214.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||||
|
google.golang.org/grpc v1.67.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.35.2 // indirect
|
||||||
|
)
|
||||||
75
golangServer/go.sum
Normal file
75
golangServer/go.sum
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||||
|
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||||
|
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
|
||||||
|
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
|
||||||
|
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||||
|
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||||
|
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
|
||||||
|
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||||
|
cloud.google.com/go/translate v1.12.3 h1:XJ7LipYJi80BCgVk2lx1fwc7DIYM6oV2qx1G4IAGQ5w=
|
||||||
|
cloud.google.com/go/translate v1.12.3/go.mod h1:qINOVpgmgBnY4YTFHdfVO4nLrSBlpvlIyosqpGEgyEg=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
|
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||||
|
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
|
||||||
|
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||||
|
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||||
|
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||||
|
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||||
|
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||||
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
|
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||||
|
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||||
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA=
|
||||||
|
google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
|
||||||
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||||
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||||
|
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||||
|
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||||
|
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||||
|
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
211
golangServer/testing.go
Normal file
211
golangServer/testing.go
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
|
||||||
|
translate "cloud.google.com/go/translate/apiv3"
|
||||||
|
"cloud.google.com/go/translate/apiv3/translatepb"
|
||||||
|
|
||||||
|
"github.com/microcosm-cc/bluemonday"
|
||||||
|
)
|
||||||
|
|
||||||
|
const project_id string = "india-translate-testing-452100"
|
||||||
|
|
||||||
|
type translationStruct struct {
|
||||||
|
En string `db:"english" json:"en"`
|
||||||
|
Hi string `db:"hindi" json:"hi"`
|
||||||
|
Bn string `db:"bengali" json:"bn"`
|
||||||
|
Mr string `db:"marathi" json:"mr"`
|
||||||
|
Ta string `db:"tamil" json:"ta"`
|
||||||
|
Te string `db:"telugu" json:"te"`
|
||||||
|
Kn string `db:"kannada" json:"kn"`
|
||||||
|
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"`
|
||||||
|
Ne string `db:"nepali" json:"ne"`
|
||||||
|
Gom string `db:"konkani" json:"gom"`
|
||||||
|
Tcy string `db:"tulu" json:"tcy"`
|
||||||
|
Bho string `db:"bhojpuri" json:"bho"`
|
||||||
|
Doi string `db:"dogri" json:"doi"`
|
||||||
|
Mni_mtei string `db:"manipuri" json:"mni-Mtei"`
|
||||||
|
Sd string `db:"sindhi" json:"sd"`
|
||||||
|
Awa string `db:"awadhi" json:"awa"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var lang_codes []string = []string{
|
||||||
|
"hi", // Hindi
|
||||||
|
"bn", // Bengali
|
||||||
|
"mr", // Marathi
|
||||||
|
"ta", // Tamil
|
||||||
|
"te", // Telugu
|
||||||
|
"kn", // Kannada
|
||||||
|
"ml", // Malayalam
|
||||||
|
"or", // Oriya
|
||||||
|
"gu", // Gujarati
|
||||||
|
"ur", // Urdu
|
||||||
|
"lus", // Mizo
|
||||||
|
"as", // Assamese
|
||||||
|
"pa", // Punjabi
|
||||||
|
"mai", // Maithili
|
||||||
|
"ne", // Nepali
|
||||||
|
"gom", // Konkani
|
||||||
|
"tcy", // Tulu
|
||||||
|
"bho", // Bhojpuri
|
||||||
|
"doi", // Dogri
|
||||||
|
"mni-Mtei", // Manipuri
|
||||||
|
"sd", // Sindhi
|
||||||
|
"awa", // Awadhi
|
||||||
|
}
|
||||||
|
|
||||||
|
var db *sqlx.DB
|
||||||
|
var p *bluemonday.Policy
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
indicates whether or not the translation exists.
|
||||||
|
*/
|
||||||
|
func getCachedTranslation(data string) (bool, translationStruct) {
|
||||||
|
prepared, err := db.Preparex("SELECT * from TRANSLATIONS WHERE english = ? COLLATE NOCASE")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
translations := translationStruct{}
|
||||||
|
err = prepared.Get(&translations, data)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return false, translations
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true, translations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToDatabase(translation translationStruct) {
|
||||||
|
_, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func translateText(text string, targetLang string) (result string, err error) {
|
||||||
|
return translateTextHelper(project_id, "en-US", targetLang, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func translateTextHelper(projectID string, sourceLang string, targetLang string, text string) (string, error) {
|
||||||
|
// projectID := "my-project-id"
|
||||||
|
// sourceLang := "en-US"
|
||||||
|
// targetLang := "fr"
|
||||||
|
// text := "Text you wish to translate"
|
||||||
|
|
||||||
|
// Instantiates a client
|
||||||
|
ctx := context.Background()
|
||||||
|
client, err := translate.NewTranslationClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("NewTranslationClient: %w", err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// Construct request
|
||||||
|
req := &translatepb.TranslateTextRequest{
|
||||||
|
Parent: fmt.Sprintf("projects/%s/locations/global", projectID),
|
||||||
|
SourceLanguageCode: sourceLang,
|
||||||
|
TargetLanguageCode: targetLang,
|
||||||
|
MimeType: "text/plain", // Mime types: "text/plain", "text/html"
|
||||||
|
Contents: []string{text},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.TranslateText(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("TranslateText: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the translation for each input text provided
|
||||||
|
return resp.GetTranslations()[0].GetTranslatedText(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
queries := r.URL.Query()
|
||||||
|
toTranslate := queries["query"][0]
|
||||||
|
toTranslate = p.Sanitize(toTranslate)
|
||||||
|
|
||||||
|
if ok, translation := getCachedTranslation(toTranslate); ok {
|
||||||
|
translationJson, _ := json.Marshal(translation)
|
||||||
|
fmt.Fprintf(w, "%v", string(translationJson))
|
||||||
|
} else {
|
||||||
|
langToTranslation := make(map[string]string)
|
||||||
|
for _, lang_code := range lang_codes {
|
||||||
|
translation, err := translateText(toTranslate, lang_code)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
langToTranslation[lang_code] = translation
|
||||||
|
}
|
||||||
|
langToTranslation["en"] = toTranslate
|
||||||
|
langToTranslationJson, _ := json.Marshal(langToTranslation)
|
||||||
|
translation := translationStruct{}
|
||||||
|
err := json.Unmarshal(langToTranslationJson, &translation)
|
||||||
|
addToDatabase(translation)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%v", string(langToTranslationJson))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Printf("Starting server...")
|
||||||
|
var err error
|
||||||
|
db, err = sqlx.Connect("sqlite3", "../translations.db")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// HTML sanitization
|
||||||
|
p = bluemonday.UGCPolicy()
|
||||||
|
|
||||||
|
// 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
281
index.html
281
index.html
@@ -3,9 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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">
|
||||||
|
|
||||||
<title>Indian Translate</title>
|
<title>Indian Translate</title>
|
||||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
<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://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js" charset="utf-8"></script>
|
||||||
|
<script src="./htmx.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background-color: #f4f4f4;
|
background-color: #f4f4f4;
|
||||||
@@ -14,33 +19,72 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
|
gap: 2em 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
border: 1px solid;
|
|
||||||
padding: 2em;
|
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 {
|
.state {
|
||||||
stroke: black;
|
stroke: black;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 1;
|
stroke-width: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.language {
|
.language {
|
||||||
stroke: red;
|
stroke: red;
|
||||||
/* There has to be a fill, even if it's transparent, to allow
|
/* There has to be a fill, even if it's transparent, to allow
|
||||||
hover events to be recognized on the inside. */
|
hover events to be recognized on the inside. */
|
||||||
fill: black;
|
/* fill: black;
|
||||||
fill-opacity: 0.0;
|
fill-opacity: 0.0; */
|
||||||
stroke-width: 2;
|
fill-opacity: 0.8;
|
||||||
|
stroke-width: 1;
|
||||||
}
|
}
|
||||||
|
.translationText, .romanizationText {
|
||||||
|
visibility: hidden;
|
||||||
|
font-family: "Noto Sans";
|
||||||
|
/* font-size:1.2em; */
|
||||||
|
/* font-size: max(0.8vmax, 16px);*/
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.romanizationText {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.languageText {
|
||||||
|
pointer-events: none;
|
||||||
|
font-weight: bold;
|
||||||
|
/* font-size: 1em;*/
|
||||||
|
font-size: max(0.7vmax, 14px);
|
||||||
|
font-family: sans-serif;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.district {
|
.district {
|
||||||
stroke: white;
|
stroke: white;
|
||||||
stroke-width: 0.5;
|
stroke-width: 0.25;
|
||||||
transition: fill 0.3s;
|
transition: fill 0.3s;
|
||||||
|
fill: none;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.language:hover {
|
.language:hover {
|
||||||
stroke-width: 3;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
.districtText {
|
.districtText {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
@@ -48,22 +92,233 @@
|
|||||||
/* .district:hover ~ .districtText {
|
/* .district:hover ~ .districtText {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
.loading-indicator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#textStuff {
|
||||||
|
min-width: 30em;
|
||||||
|
/* flex: 0 1 0; */
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#textStuff ul {
|
||||||
|
font-size: 1.2em;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
max-width: 35em;
|
||||||
|
}
|
||||||
|
|
||||||
|
section#textStuff li {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#query {
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 6px 8px;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitButton {
|
||||||
|
appearance: none;
|
||||||
|
background-color: #2ea44f;
|
||||||
|
border: 1px solid rgba(27, 31, 35, .15);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: rgba(27, 31, 35, .1) 0 1px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2em;
|
||||||
|
padding: 6px 16px;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
touch-action: manipulation;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitButton:focus:not(:focus-visible):not(.focus-visible) {
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitButton:hover {
|
||||||
|
background-color: #2c974b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitButton:focus {
|
||||||
|
box-shadow: rgba(46, 164, 79, .4) 0 0 0 3px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitButton:disabled {
|
||||||
|
background-color: #94d3a2;
|
||||||
|
border-color: rgba(27, 31, 35, .1);
|
||||||
|
color: rgba(255, 255, 255, .8);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitButton:active {
|
||||||
|
background-color: #298e46;
|
||||||
|
box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position map load spinner in the middle of the SVG */
|
||||||
|
#svgContainer {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
flex-shrink: 1;
|
||||||
|
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: 1366px) {
|
||||||
|
main {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.district {
|
||||||
|
stroke-width: 0.1;
|
||||||
|
}
|
||||||
|
.translationText, .romanizationText {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
.languageText {
|
||||||
|
font-size: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @media only screen and (max-width: 768px) {
|
||||||
|
.translationText, .romanizationText {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
.languageText {
|
||||||
|
font-size: 0.6em;
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
p.bottom-of-page {
|
||||||
|
padding-top: 1em;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.15em;
|
||||||
|
color: #3D3C3A;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<section id="textStuff">
|
<section id="textStuff">
|
||||||
<h1>Indian Translate</h1>
|
<h1>Indian Translate</h1>
|
||||||
|
<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" maxlength="20" name="query" id="query" required/>
|
||||||
|
<input type="submit" id="submitButton" 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>
|
||||||
|
<ul>
|
||||||
|
<li>Enter text in English. Translations are displayed on the map, along with romanizations (translation converted to Latin script).</li>
|
||||||
|
<li>Hover over a language region (denoted by color) to see the language spoken in the region. Language boundaries were drawn using census data; they may be inaccurate.</li>
|
||||||
|
<li>Translations are retrieved from Google Translate - they may be innacurate.</li>
|
||||||
|
<li>Romanizations are made with the <kbd>ai4bharat-transliteration</kbd> AI model, developed by <a href="https://ai4bharat.iitm.ac.in/">Ai4Bharat</a>.</li>
|
||||||
|
<li>This project was inspired by the <a href="https://ukdataexplorer.com/european-translator/">European Translation Map</a>, as seen on Hacker News.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="bottom-of-page">Made with love by <a href="https://twomorecents.org">Aadhavan Srinivasan</a></p>
|
||||||
|
|
||||||
<label for="toTranslate">Enter text to translate:
|
|
||||||
<input type="text" name="toTranslate"/>
|
|
||||||
</label>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<svg id = "indiaMap" width="700" height="700"></svg>
|
<section id="svgContainer">
|
||||||
|
<div id="mapLoadSpinner" class="mapLoadSpinner"></div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="index.js"></script>
|
<script type="text/javascript" src="index.js"></script>
|
||||||
|
<script type="text/javascript" src="updateTranslations.js"></script>
|
||||||
|
<script type="text/javascript" src="svgHoverListener.js"></script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
416
index.js
416
index.js
@@ -1,56 +1,28 @@
|
|||||||
const svg = d3.select("svg")
|
const defaultColor = "#555555"
|
||||||
|
|
||||||
const tamilColor = "#75d795" // Tamil
|
|
||||||
const malayalamColor = "#ff7c7c" // Malayalam
|
|
||||||
const kannadaColor = "#ffe77c" // Kannada
|
|
||||||
const teluguColor = "#7c9dff" // Telugu
|
|
||||||
const marathiColor = "#e0ff7c" // Marathi
|
|
||||||
const konkaniColor = "#9b7cff" // Konkani
|
|
||||||
const hindiColor = "#d17cff" // Hindi
|
|
||||||
const gujaratiColor = "#7cffee" // Gujarati
|
|
||||||
const marwariColor = "#7bc4c9" // Marwari
|
|
||||||
const oriyaColor = "#9bcc9f" // Oriya
|
|
||||||
const bengaliColor = "#bf9a77" // Bengali
|
|
||||||
const punjabiColor = "#e84a35" // Punjabi
|
|
||||||
const mizoColor = "#a6a4de" // Mizo
|
|
||||||
const assameseColor = "#c9535b" // Assamese
|
|
||||||
const bhojpuriColor = "#b3b876" // Bhojpuri
|
|
||||||
const manipuriColor = "#c9afad" // Manipuri
|
|
||||||
const dogriColor = "#9595e6" // Dogri (near Kashmir)
|
|
||||||
const nepaliColor = "#71998e" // Nepali
|
|
||||||
const urduColor = "#3fa179" // Urdu
|
|
||||||
const tuluColor = "#dedc52" // Tulu
|
|
||||||
const maithaliColor = "#4472a6" // Maithali
|
|
||||||
const santaliColor = "#96bf60" // Santhali
|
|
||||||
const sindhiColor = "#e89931" // Sindhi
|
|
||||||
|
|
||||||
const unavailLangColor = "#555555"
|
|
||||||
|
|
||||||
const languages = {
|
const languages = {
|
||||||
tamil: {name: "Tamil", color: tamilColor, districts: []},
|
tamil: {name: "Tamil", color: "#75d795", code: "ta", text_align: "start", districts: []},
|
||||||
malayalam: {name: "Malayalam", color: malayalamColor, districts: []},
|
malayalam: {name: "Malayalam", color: "#ff7c7c", code: "ml", text_align: "end", districts: []},
|
||||||
kannada: {name: "Kannada", color: kannadaColor, districts: []},
|
kannada: {name: "Kannada", color: "#ffe77c", code: "kn", text_align: "middle", districts: []},
|
||||||
telugu: {name: "Telugu", color: teluguColor, districts: []},
|
telugu: {name: "Telugu", color: "#7c9dff", code: "te", text_align: "start", districts: []},
|
||||||
marathi: {name: "Marathi", color: marathiColor, districts: []},
|
marathi: {name: "Marathi", color: "#e0ff7c", code: "mr", text_align: "middle", districts: []},
|
||||||
konkani: {name: "Konkani", color: konkaniColor, districts: []},
|
konkani: {name: "Konkani", color: "#9b7cff", code: "gom", text_align: "end", districts: []},
|
||||||
hindi: {name: "Hindi", color: hindiColor, districts: []},
|
hindi: {name: "Hindi", color: "#d17cff", code: "hi", text_align: "middle", districts: []},
|
||||||
gujarati: {name: "Gujarati", color: gujaratiColor, districts: []},
|
gujarati: {name: "Gujarati", color: "#7cffee", code: "gu", text_align: "middle", districts: []},
|
||||||
marwari: {name: "Marwari", color: marwariColor, districts: []},
|
oriya: {name: "Oriya", color: "#9bcc9f", code: "or", text_align: "middle", districts: []},
|
||||||
oriya: {name: "Oriya", color: oriyaColor, districts: []},
|
bengali: {name: "Bengali", color: "#bf9a77", code: "bn", text_align: "middle", districts: []},
|
||||||
bengali: {name: "Bengali", color: bengaliColor, districts: []},
|
punjabi: {name: "Punjabi", color: "#e84a35", code: "pa", text_align: "middle", districts: []},
|
||||||
punjabi: {name: "Punjabi", color: punjabiColor, districts: []},
|
mizo: {name: "Mizo", color: "#a6a4de", code: "lus", text_align: "middle", districts: []},
|
||||||
mizo: {name: "Mizo", color: mizoColor, districts: []},
|
assamese: {name: "Assamese", color: "#c9535b", code: "as", text_align: "middle", districts: []},
|
||||||
assamese: {name: "Assamese", color: assameseColor, districts: []},
|
bhojpuri: {name: "Bhojpuri", color: "#b3b876", code: "bho", text_align: "end", districts: []},
|
||||||
bhojpuri: {name: "Bhojpuri", color: bhojpuriColor, districts: []},
|
manipuri: {name: "Manipuri", color: "#c9afad", code: "mni-Mtei", text_align: "middle", districts: []},
|
||||||
manipuri: {name: "Manipuri", color: manipuriColor, districts: []},
|
dogri: {name: "Dogri", color: "#9595e6", code: "doi", text_align: "middle", districts: []},
|
||||||
dogri: {name: "Dogri", color: dogriColor, districts: []},
|
nepali: {name: "Nepali", color: "#71998e", code: "ne", text_align: "middle", districts: []},
|
||||||
nepali: {name: "Nepali", color: nepaliColor, districts: []},
|
urdu: {name: "Urdu", color: "#3fa179", code: "ur", text_align: "middle", districts: []},
|
||||||
urdu: {name: "Urdu", color: urduColor, districts: []},
|
tulu: {name: "Tulu", color: "#dedc52", code: "tcy", text_align: "end", districts: []},
|
||||||
tulu: {name: "Tulu", color: tuluColor, districts: []},
|
maithali: {name: "Maithali", color: "#4472a6", code: "mai", text_align: "middle", districts: []},
|
||||||
maithali: {name: "Maithali", color: maithaliColor, districts: []},
|
sindhi: {name: "Sindhi", color: "#e89931", code: "sd", text_align: "middle", districts: []},
|
||||||
santali: {name: "Santali", color: santaliColor, districts: []},
|
awadhi: {name: "Awadhi", color: "#847fb5", code: "awa", text_align: "middle", districts: []},
|
||||||
sindhi: {name: "Sindhi", color: sindhiColor, districts: []},
|
|
||||||
unavail: {name: "Unavailable", color: unavailLangColor, districts: []},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Credit: https://www.artcraftblend.com/blogs/colors/shades-of-pastel
|
// Credit: https://www.artcraftblend.com/blogs/colors/shades-of-pastel
|
||||||
@@ -64,17 +36,14 @@ const state2lang = {
|
|||||||
"Goa": languages["konkani"],
|
"Goa": languages["konkani"],
|
||||||
"Odisha": languages["oriya"],
|
"Odisha": languages["oriya"],
|
||||||
"Gujarat": languages["gujarati"],
|
"Gujarat": languages["gujarati"],
|
||||||
"Rajasthan": languages["marwari"],
|
"Rajasthan": languages["hindi"],
|
||||||
"Chhattisgarh": languages["hindi"],
|
"Chhattisgarh": languages["hindi"],
|
||||||
"Jharkhand": languages["hindi"], // DEFAULT
|
"Jharkhand": languages["hindi"], // DEFAULT
|
||||||
"West Bengal": languages["bengali"],
|
"West Bengal": languages["bengali"],
|
||||||
"Assam": languages["assamese"],
|
"Assam": languages["assamese"],
|
||||||
"Meghalaya": languages["unavail"], // DEFAULT
|
|
||||||
"Tripura": languages["bengali"],
|
"Tripura": languages["bengali"],
|
||||||
"Mizoram": languages["mizo"],
|
"Mizoram": languages["mizo"],
|
||||||
"Manipur": languages["manipuri"],
|
"Manipur": languages["manipuri"],
|
||||||
"Nagaland": languages["unavail"], // DEFAULT
|
|
||||||
"Arunachal Pradesh": languages["unavail"], // DEFAULT
|
|
||||||
"Sikkim": languages["nepali"],
|
"Sikkim": languages["nepali"],
|
||||||
"Bihar": languages["bhojpuri"],
|
"Bihar": languages["bhojpuri"],
|
||||||
"Madhya Pradesh": languages["hindi"],
|
"Madhya Pradesh": languages["hindi"],
|
||||||
@@ -89,10 +58,8 @@ const state2lang = {
|
|||||||
"Dadra and Nagar Haveli and Daman and Diu": languages["gujarati"],
|
"Dadra and Nagar Haveli and Daman and Diu": languages["gujarati"],
|
||||||
"Puducherry": languages["tamil"],
|
"Puducherry": languages["tamil"],
|
||||||
"Lakshadweep": languages["malayalam"],
|
"Lakshadweep": languages["malayalam"],
|
||||||
"Andaman and Nicobar Islands": languages["unavail"],
|
|
||||||
"Delhi": languages["hindi"],
|
"Delhi": languages["hindi"],
|
||||||
"Chandigarh": languages["hindi"]
|
"Chandigarh": languages["hindi"]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const district2lang = { // Should override state colors
|
const district2lang = { // Should override state colors
|
||||||
@@ -147,21 +114,47 @@ const district2lang = { // Should override state colors
|
|||||||
|
|
||||||
"Kutch": languages["sindhi"],
|
"Kutch": languages["sindhi"],
|
||||||
|
|
||||||
"Godda": languages["santali"],
|
"Kanpur": languages["awadhi"],
|
||||||
"Deoghar": languages["santali"],
|
"Lakhimpur Kheri": languages["awadhi"],
|
||||||
"Dumka": languages["santali"],
|
"Sitapur": languages["awadhi"],
|
||||||
"Jamtara": languages["santali"],
|
"Hardoi": languages["awadhi"],
|
||||||
"Sahibganj": languages["santali"],
|
"Unnao": languages["awadhi"],
|
||||||
"Pakur": languages["santali"],
|
"Fatehpur": languages["awadhi"],
|
||||||
"East Singhbhum": languages["santali"],
|
"Barabanki": languages["awadhi"],
|
||||||
"Jhargram": languages["santali"],
|
"Lucknow": languages["awadhi"],
|
||||||
"Bankura": languages["santali"],
|
"Rae Bareli": languages["awadhi"],
|
||||||
"Purulia": languages["santali"],
|
"Amethi": languages["awadhi"],
|
||||||
|
"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) {
|
function reverseCoordArrays(coords) {
|
||||||
if (!Array.isArray(coords)) {
|
if (!Array.isArray(coords)) {
|
||||||
return coords;
|
return coords;
|
||||||
@@ -177,7 +170,6 @@ function reverseCoordArrays(coords) {
|
|||||||
return coords.map(reverseCoordArrays);
|
return coords.map(reverseCoordArrays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getOuterBoundaryPolygon(features) {
|
function getOuterBoundaryPolygon(features) {
|
||||||
// Check if we have features to process
|
// Check if we have features to process
|
||||||
if (!features || features.length === 0) {
|
if (!features || features.length === 0) {
|
||||||
@@ -196,6 +188,15 @@ function getOuterBoundaryPolygon(features) {
|
|||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function district2langFunc(d) {
|
||||||
|
if (district2lang.hasOwnProperty(d.properties.district)) {
|
||||||
|
return district2lang[d.properties.district];
|
||||||
|
} else if (state2lang.hasOwnProperty(d.properties.st_nm)) {
|
||||||
|
return state2lang[d.properties.st_nm];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function stateOrDistrictOrLanguage(d) {
|
function stateOrDistrictOrLanguage(d) {
|
||||||
if (typeof d.properties.district !== 'undefined') {
|
if (typeof d.properties.district !== 'undefined') {
|
||||||
@@ -209,12 +210,50 @@ function stateOrDistrictOrLanguage(d) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// const mapWidth = document.getElementById("indiaMap").getAttribute("width")
|
||||||
|
// const mapHeight = document.getElementById("indiaMap").getAttribute("height")
|
||||||
|
let mapWidth = /*window.innerWidth - */document.querySelector("#svgContainer").offsetWidth * 0.85;
|
||||||
|
// const mapHeight = document.querySelector("#svgContainer").offsetHeight;
|
||||||
|
let mapHeight = (window.innerHeight - document.querySelector("#svgContainer").getBoundingClientRect().top);
|
||||||
|
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
mapWidth *= 1.5;
|
||||||
|
mapHeight *= 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
function drawMap(world) {
|
||||||
const mapWidth = document.getElementById("indiaMap").getAttribute("width")
|
let projection = null;
|
||||||
const mapHeight = document.getElementById("indiaMap").getAttribute("height")
|
if (window.innerWidth > 1366) {
|
||||||
const projection = d3.geoMercator().fitSize([mapWidth, mapHeight], world)
|
projection = d3.geoMercator().fitSize([mapWidth, mapHeight], world)
|
||||||
|
} else {
|
||||||
|
projection = d3.geoMercator().fitWidth(mapWidth, world)
|
||||||
|
}
|
||||||
const path = d3.geoPath().projection(projection);
|
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", bbox.width)
|
||||||
|
.attr("height", bbox.height)
|
||||||
|
// .style("display", "block")
|
||||||
|
// .style("max-height", "100%"); // optional: keep it scalable in a flexbox
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const states = svg.selectAll("g")
|
const states = svg.selectAll("g")
|
||||||
.data(world.features)
|
.data(world.features)
|
||||||
.enter()
|
.enter()
|
||||||
@@ -223,55 +262,216 @@ function drawMap(world) {
|
|||||||
states.append("path")
|
states.append("path")
|
||||||
.attr("d", path)
|
.attr("d", path)
|
||||||
.attr("class", d => stateOrDistrictOrLanguage(d))
|
.attr("class", d => stateOrDistrictOrLanguage(d))
|
||||||
.attr("fill", function(d) {
|
.attr("id", function(d) {
|
||||||
if (stateOrDistrictOrLanguage(d) === "district") {
|
if (stateOrDistrictOrLanguage(d) === "language") {
|
||||||
return district2lang.hasOwnProperty(d.properties.district) ?
|
return languages[d.properties.lang_name.toLowerCase()].code + "langMap";
|
||||||
district2lang[d.properties.district].color :
|
|
||||||
state2lang[d.properties.st_nm].color
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.attr("fill", function(d) {
|
||||||
|
if (stateOrDistrictOrLanguage(d) === "language") {
|
||||||
|
return languages[d.properties.lang_name.toLowerCase()].color;
|
||||||
|
}
|
||||||
|
})
|
||||||
.each(function(d) {
|
.each(function(d) {
|
||||||
if (stateOrDistrictOrLanguage(d) === "district") {
|
if (stateOrDistrictOrLanguage(d) === "district") {
|
||||||
districtLang = district2lang.hasOwnProperty(d.properties.district) ? district2lang[d.properties.district] : state2lang[d.properties.st_nm];
|
const districtLang = district2langFunc(d);
|
||||||
districtLang.districts.push(d)
|
if (typeof districtLang !== 'undefined') {
|
||||||
|
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
|
.append("title") // Tooltip
|
||||||
.text(d => d.properties.district);
|
.text(d => d.properties.district);
|
||||||
|
|
||||||
states.append("text")
|
const textItems = svg.selectAll("g.textItem").data(world.features).enter().append("g");
|
||||||
.attr("x", d => projection(d3.geoCentroid(d))[0])
|
textItems.append("text")
|
||||||
.attr("y", d => projection(d3.geoCentroid(d))[1])
|
.attr("x", function(d) {
|
||||||
.attr("text-anchor", "middle")
|
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||||
.attr("font-size", "12px")
|
rtv = projection(d3.geoCentroid(d))[0];
|
||||||
|
if (d.properties.lang_name == "Kannada") {
|
||||||
|
rtv -= 20;
|
||||||
|
}
|
||||||
|
if (d.properties.lang_name == "Tamil") {
|
||||||
|
rtv -= 10;
|
||||||
|
}
|
||||||
|
if (d.properties.lang_name == "Kannada" && window.innerWidth <= 768) {
|
||||||
|
rtv += 15;
|
||||||
|
}
|
||||||
|
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") {
|
||||||
|
if (window.innerWidth > 768) {
|
||||||
|
rtv += 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (d.properties.lang_name == "Tamil") {
|
||||||
|
rtv -= 10;
|
||||||
|
}
|
||||||
|
if (d.properties.lang_name == "Malayalam") {
|
||||||
|
rtv += 10;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (d.properties.lang_name == "Hindi") {
|
||||||
|
if (window.innerWidth > 768) {
|
||||||
|
rtv += 3 * parseFloat(getComputedStyle(document.documentElement).fontSize); // rem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtv
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.attr("class", "translationText")
|
||||||
|
.attr("id", function(d) {
|
||||||
|
if (stateOrDistrictOrLanguage(d) == "language") {
|
||||||
|
return d.properties.lang_code+"Text"
|
||||||
|
} else {
|
||||||
|
d3.select(this).remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.attr("text-anchor", d => typeof d.properties.lang_name != "undefined" && languages[d.properties.lang_name.toLowerCase()].text_align)
|
||||||
|
.attr("fill", "black")
|
||||||
|
|
||||||
|
// Romanization
|
||||||
|
textItems.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("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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.attr("text-anchor", d => typeof d.properties.lang_name != "undefined" && languages[d.properties.lang_name.toLowerCase()].text_align)
|
||||||
|
.attr("fill", "black")
|
||||||
|
|
||||||
|
// Language
|
||||||
|
textItems.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("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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.attr("text-anchor", d => typeof d.properties.lang_name != "undefined" && languages[d.properties.lang_name.toLowerCase()].text_align)
|
||||||
.attr("fill", "black")
|
.attr("fill", "black")
|
||||||
.attr("class", "districtText")
|
|
||||||
.attr("id", d => d.properties.district+"Text")
|
|
||||||
.text(d => d.properties.district);
|
|
||||||
|
|
||||||
let allLangs = []
|
let allLangs = []
|
||||||
|
|
||||||
// for (const [langId,lang] of Object.entries(languages)) {
|
const coordinates = [77.69916967457782,23.389970772934166];
|
||||||
// let geojson = {
|
const [xCoord, yCoord] = projection(coordinates);
|
||||||
// "type": "FeatureCollection",
|
|
||||||
// "features": lang.districts
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// let outerBound = getOuterBoundaryPolygon(geojson.features)
|
|
||||||
// outerBound["id"] = "lang" + lang.name
|
|
||||||
// outerBound.properties["lang_name"]= lang.name
|
|
||||||
// allLangs.push(outerBound);
|
|
||||||
|
|
||||||
// svg.append("path")
|
// Add a listener to toggle the language display when a language is hovered on the map
|
||||||
// .datum(outerBound)
|
document.querySelectorAll(".language").
|
||||||
// .attr("d", path)
|
forEach(function(element) {
|
||||||
// .attr("fill", "none")
|
element.addEventListener("mouseover", toggleLanguageDisplay);
|
||||||
// .attr("stroke", "red")
|
element.addEventListener("mouseout", toggleLanguageDisplay);
|
||||||
// .attr("stroke-width", 2)
|
});
|
||||||
// }
|
|
||||||
// console.log(JSON.stringify(allLangs))
|
// Remove all 'g' elements that have no children (they don't have a language, translation, or romanization). This must mean they accidentally got created when I created the 'textItems' variable
|
||||||
|
document.querySelectorAll("g").
|
||||||
|
forEach(function(element) {
|
||||||
|
if (element.childElementCount == 0) {
|
||||||
|
element.remove()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 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("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
|
Assamese
|
||||||
Punjabi
|
Punjabi
|
||||||
Maithili
|
Maithili
|
||||||
Santali
|
|
||||||
Nepali
|
Nepali
|
||||||
Konkani
|
Konkani
|
||||||
Tulu
|
Tulu
|
||||||
@@ -21,3 +20,4 @@ Bhojpuri
|
|||||||
Dogri
|
Dogri
|
||||||
Manipuri
|
Manipuri
|
||||||
Sindhi
|
Sindhi
|
||||||
|
Awadhi
|
||||||
|
|||||||
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)
|
||||||
8
svgHoverListener.js
Normal file
8
svgHoverListener.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
function toggleLanguageDisplay(event) {
|
||||||
|
if (event.type == "mouseover") {
|
||||||
|
document.getElementById(this.id.replace(/langMap$/, '') + "Language").style.visibility = "visible";
|
||||||
|
}
|
||||||
|
if (event.type == "mouseout") {
|
||||||
|
document.getElementById(this.id.replace(/langMap$/, '') + "Language").style.visibility = "hidden";
|
||||||
|
}
|
||||||
|
}
|
||||||
7
todo.txt
Normal file
7
todo.txt
Normal file
@@ -0,0 +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; 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
BIN
translations.db
Normal file
Binary file not shown.
37
updateTranslations.js
Normal file
37
updateTranslations.js
Normal file
@@ -0,0 +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(".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"
|
||||||
|
});
|
||||||
|
}
|
||||||
290
xlit_server.py
Normal file
290
xlit_server.py
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
"""
|
||||||
|
Expose Transliteration Engine as an HTTP API.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
```
|
||||||
|
from ai4bharat.transliteration import xlit_server
|
||||||
|
app, engine = xlit_server.get_app()
|
||||||
|
app.run(host='0.0.0.0', port=8000)
|
||||||
|
```
|
||||||
|
Sample URLs:
|
||||||
|
http://localhost:8000/tl/ta/amma
|
||||||
|
http://localhost:8000/languages
|
||||||
|
|
||||||
|
FORMAT:
|
||||||
|
Based on the Varnam API standard
|
||||||
|
https://api.varnamproject.com/tl/hi/bharat
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, request, make_response
|
||||||
|
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
|
||||||
|
|
||||||
|
class XlitError(enum.Enum):
|
||||||
|
lang_err = "Unsupported langauge ID requested ;( Please check available languages."
|
||||||
|
string_err = "String passed is incompatable ;("
|
||||||
|
internal_err = "Internal crash ;("
|
||||||
|
unknown_err = "Unknown Failure"
|
||||||
|
loading_err = "Loading failed ;( Check if metadata/paths are correctly configured."
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['JSON_AS_ASCII'] = False
|
||||||
|
|
||||||
|
## ----------------------------- Xlit Engine -------------------------------- ##
|
||||||
|
|
||||||
|
from .xlit_src import XlitEngine
|
||||||
|
|
||||||
|
MAX_SUGGESTIONS = 8
|
||||||
|
DEFAULT_NUM_SUGGESTIONS = 5
|
||||||
|
|
||||||
|
ENGINE = {
|
||||||
|
"en2indic": XlitEngine(beam_width=MAX_SUGGESTIONS, rescore=True, model_type="transformer", src_script_type = "roman"),
|
||||||
|
"indic2en": XlitEngine(beam_width=MAX_SUGGESTIONS, rescore=True, model_type="transformer", src_script_type = "indic"),
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPOSED_LANGS = [
|
||||||
|
{
|
||||||
|
"LangCode": lang_code, # ISO-639 code
|
||||||
|
"Identifier": lang_code, # ISO-639 code
|
||||||
|
"DisplayName": LANG_CODE_TO_DISPLAY_NAME[lang_code],
|
||||||
|
"Author": "AI4Bharat", # Name of developer / team
|
||||||
|
"CompiledDate": "09-April-2022", # date on which model was trained
|
||||||
|
"IsStable": True, # Set `False` if the model is experimental
|
||||||
|
"Direction": "rtl" if lang_code in RTL_LANG_CODES else "ltr",
|
||||||
|
"ScriptCode": LANG_CODE_TO_SCRIPT_CODE[lang_code],
|
||||||
|
} for lang_code in sorted(ENGINE["en2indic"].all_supported_langs)
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_app():
|
||||||
|
return app, ENGINE
|
||||||
|
|
||||||
|
def get_only_app():
|
||||||
|
return app
|
||||||
|
|
||||||
|
## ---------------------------- API End-points ------------------------------ ##
|
||||||
|
|
||||||
|
@app.route('/languages', methods = ['GET', 'POST'])
|
||||||
|
def supported_languages():
|
||||||
|
# Format - https://xlit-api.ai4bharat.org/languages
|
||||||
|
response = make_response(jsonify(EXPOSED_LANGS))
|
||||||
|
if 'xlit_user_id' not in request.cookies:
|
||||||
|
# host = request.environ['HTTP_ORIGIN'].split('://')[1]
|
||||||
|
host = '.ai4bharat.org'
|
||||||
|
response.set_cookie('xlit_user_id', uuid4().hex, max_age=365*24*60*60, domain=host, samesite='None', secure=True, httponly=True)
|
||||||
|
return response
|
||||||
|
|
||||||
|
@app.route('/tl/<lang_code>/<eng_word>', methods = ['GET', 'POST'])
|
||||||
|
def xlit_api(lang_code, eng_word):
|
||||||
|
# Format: https://xlit-api.ai4bharat.org/tl/ta/bharat
|
||||||
|
response = {
|
||||||
|
'success': False,
|
||||||
|
'error': '',
|
||||||
|
'at': str(datetime.utcnow()) + ' +0000 UTC',
|
||||||
|
'input': eng_word.strip(),
|
||||||
|
'result': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
transliterate_numerals = request.args.get('transliterate_numerals', default=False, type=lambda v: v.lower() == 'true')
|
||||||
|
num_suggestions = request.args.get('num_suggestions', default=DEFAULT_NUM_SUGGESTIONS, type=int)
|
||||||
|
|
||||||
|
if lang_code not in ENGINE["en2indic"].all_supported_langs:
|
||||||
|
response['error'] = 'Invalid scheme identifier. Supported languages are: '+ str(ENGINE["en2indic"].all_supported_langs)
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
try:
|
||||||
|
## Limit char count to --> 70
|
||||||
|
xlit_result = ENGINE["en2indic"].translit_word(eng_word[:70], lang_code, topk=num_suggestions, transliterate_numerals=transliterate_numerals)
|
||||||
|
except Exception as e:
|
||||||
|
xlit_result = XlitError.internal_err
|
||||||
|
|
||||||
|
|
||||||
|
if isinstance(xlit_result, XlitError):
|
||||||
|
response['error'] = xlit_result.value
|
||||||
|
print("XlitError:", traceback.format_exc())
|
||||||
|
else:
|
||||||
|
response['result'] = xlit_result
|
||||||
|
response['success'] = True
|
||||||
|
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
@app.route('/rtl/<lang_code>/<word>', methods = ['GET', 'POST'])
|
||||||
|
def reverse_xlit_api(lang_code, word):
|
||||||
|
# Format: https://api.varnamproject.com/rtl/hi/%E0%A4%AD%E0%A4%BE%E0%A4%B0%E0%A4%A4
|
||||||
|
response = {
|
||||||
|
'success': False,
|
||||||
|
'error': '',
|
||||||
|
'at': str(datetime.utcnow()) + ' +0000 UTC',
|
||||||
|
'input': word.strip(),
|
||||||
|
'result': ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if lang_code not in ENGINE["indic2en"].all_supported_langs:
|
||||||
|
response['error'] = 'Invalid scheme identifier. Supported languages are: '+ str(ENGINE["indic2en"].all_supported_langs)
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
num_suggestions = request.args.get('num_suggestions', default=DEFAULT_NUM_SUGGESTIONS, type=int)
|
||||||
|
|
||||||
|
try:
|
||||||
|
## Limit char count to --> 70
|
||||||
|
xlit_result = ENGINE["indic2en"].translit_sentence(word, lang_code)
|
||||||
|
except Exception as e:
|
||||||
|
xlit_result = XlitError.internal_err
|
||||||
|
|
||||||
|
if isinstance(xlit_result, XlitError):
|
||||||
|
response['error'] = xlit_result.value
|
||||||
|
print("XlitError:", traceback.format_exc())
|
||||||
|
else:
|
||||||
|
response['result'] = xlit_result
|
||||||
|
response['success'] = True
|
||||||
|
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
@app.route('/transliterate', methods=['POST'])
|
||||||
|
def ulca_api():
|
||||||
|
'''
|
||||||
|
ULCA-compliant endpoint. See for sample request-response:
|
||||||
|
https://github.com/ULCA-IN/ulca/tree/master/specs/examples/model/transliteration-model
|
||||||
|
'''
|
||||||
|
data = request.get_json(force=True)
|
||||||
|
|
||||||
|
if "input" not in data or "config" not in data:
|
||||||
|
return jsonify({
|
||||||
|
"status": {
|
||||||
|
"statusCode": 400,
|
||||||
|
"message": "Ensure `input` and `config` fields missing."
|
||||||
|
}
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
if (data["config"]["language"]["sourceLanguage"] == "en" and data["config"]["language"]["targetLanguage"] in ENGINE["en2indic"].all_supported_langs) or (data["config"]["language"]["sourceLanguage"] in ENGINE["indic2en"].all_supported_langs and data["config"]["language"]["targetLanguage"] == 'en'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
"status": {
|
||||||
|
"statusCode": 501,
|
||||||
|
"message": "The mentioned language-pair is not supported yet."
|
||||||
|
}
|
||||||
|
}), 501
|
||||||
|
|
||||||
|
is_sentence = data["config"]["isSentence"] if "isSentence" in data["config"] else False
|
||||||
|
num_suggestions = 1 if is_sentence else (data["config"]["numSuggestions"] if "numSuggestions" in data["config"] else 5)
|
||||||
|
|
||||||
|
if data["config"]["language"]["targetLanguage"] == "en":
|
||||||
|
engine = ENGINE["indic2en"]
|
||||||
|
lang_code = data["config"]["language"]["sourceLanguage"]
|
||||||
|
else:
|
||||||
|
engine = ENGINE["en2indic"]
|
||||||
|
lang_code = data["config"]["language"]["targetLanguage"]
|
||||||
|
|
||||||
|
outputs = []
|
||||||
|
for item in data["input"]:
|
||||||
|
if is_sentence:
|
||||||
|
item["target"] = [engine.translit_sentence(item["source"], lang_code=lang_code)]
|
||||||
|
else:
|
||||||
|
item["source"] = item["source"][:32]
|
||||||
|
item["target"] = engine.translit_word(item["source"], lang_code=lang_code, topk=num_suggestions)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"output": data["input"],
|
||||||
|
# "status": {
|
||||||
|
# "statusCode": 200,
|
||||||
|
# "message" : "success"
|
||||||
|
# }
|
||||||
|
}, 200
|
||||||
|
|
||||||
|
@app.route('/romanize', methods=['POST'])
|
||||||
|
def romanizeHandler():
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user