A bunch of vocab, and (working in dev) page for adding vocab
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s

This commit is contained in:
ryan 2025-09-30 16:38:27 -07:00
parent 15f7d2e679
commit d302c19265
15 changed files with 1151 additions and 52 deletions

View File

@ -1 +0,0 @@
/nix/store/ds5hg5lwbpkp0pmwvq1r1khdj9dhgr68-source

View File

@ -1 +0,0 @@
/nix/store/piq8ffvrjw88nxj69ib64pfqg7vj19mp-source

View File

@ -1 +1 @@
/nix/store/z7x7krys4bv786dsqsb015ancd5glyab-nix-shell-env
/nix/store/2l63479bllklr4bzi4vscn7ndj6lg614-nix-shell-env

View File

@ -15,7 +15,7 @@ export CONFIG_SHELL
CXX='g++'
export CXX
HOSTTYPE='x86_64'
HOST_PATH='/nix/store/ddx7976jyll30xjbasghv9jailswprcp-bash-interactive-5.3p3/bin:/nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0/bin:/nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/bin:/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/bin:/nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1/bin:/nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/bin:/nix/store/jq2kbdw6ljv9i47jz23pm072cfyxwpfj-postgresql-17.6/bin:/nix/store/n4lyyqirbz2j0igs12m2pyqrs7zyyvld-netlify-cli-19.0.2/bin:/nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin'
HOST_PATH='/nix/store/ddx7976jyll30xjbasghv9jailswprcp-bash-interactive-5.3p3/bin:/nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0/bin:/nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/bin:/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/bin:/nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1/bin:/nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/bin:/nix/store/jq2kbdw6ljv9i47jz23pm072cfyxwpfj-postgresql-17.6/bin:/nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/bin:/nix/store/1p5n2mzy33ayzc1scdnz82h53d192knh-claude-code-1.0.117/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin'
export HOST_PATH
IFS='
'
@ -37,7 +37,7 @@ NIX_CC='/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0'
export NIX_CC
NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu='1'
export NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
NIX_CFLAGS_COMPILE=' -frandom-seed=z7x7krys4b -isystem /nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev/include -isystem /nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/include -isystem /nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/include -isystem /nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/include -isystem /nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/include -isystem /nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev/include -isystem /nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/include -isystem /nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/include -isystem /nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/include -isystem /nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/include'
NIX_CFLAGS_COMPILE=' -frandom-seed=2l63479bll -isystem /nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev/include -isystem /nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/include -isystem /nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/include -isystem /nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/include -isystem /nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/include -isystem /nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev/include -isystem /nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/include -isystem /nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/include -isystem /nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/include -isystem /nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/include'
export NIX_CFLAGS_COMPILE
NIX_ENFORCE_NO_NATIVE='1'
export NIX_ENFORCE_NO_NATIVE
@ -50,7 +50,7 @@ NIX_STORE='/nix/store'
export NIX_STORE
NM='nm'
export NM
NODE_PATH='/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/lib/node_modules:/nix/store/n4lyyqirbz2j0igs12m2pyqrs7zyyvld-netlify-cli-19.0.2/lib/node_modules'
NODE_PATH='/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/lib/node_modules:/nix/store/1p5n2mzy33ayzc1scdnz82h53d192knh-claude-code-1.0.117/lib/node_modules'
export NODE_PATH
OBJCOPY='objcopy'
export OBJCOPY
@ -60,7 +60,7 @@ OLDPWD=''
export OLDPWD
OPTERR='1'
OSTYPE='linux-gnu'
PATH='/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2/bin:/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0/bin:/nix/store/82kmz7r96navanrc2fgckh2bamiqrgsw-gcc-14.3.0/bin:/nix/store/4jxivbjpr86wmsziqlf7iljlwjlxz8bh-glibc-2.40-66-bin/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44/bin:/nix/store/c43ry7z24x3jhnjlj4gpay8a4g2p3x1h-binutils-2.44/bin:/nix/store/ddx7976jyll30xjbasghv9jailswprcp-bash-interactive-5.3p3/bin:/nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0/bin:/nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/bin:/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/bin:/nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1/bin:/nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/bin:/nix/store/jq2kbdw6ljv9i47jz23pm072cfyxwpfj-postgresql-17.6/bin:/nix/store/n4lyyqirbz2j0igs12m2pyqrs7zyyvld-netlify-cli-19.0.2/bin:/nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin'
PATH='/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2/bin:/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0/bin:/nix/store/82kmz7r96navanrc2fgckh2bamiqrgsw-gcc-14.3.0/bin:/nix/store/4jxivbjpr86wmsziqlf7iljlwjlxz8bh-glibc-2.40-66-bin/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44/bin:/nix/store/c43ry7z24x3jhnjlj4gpay8a4g2p3x1h-binutils-2.44/bin:/nix/store/ddx7976jyll30xjbasghv9jailswprcp-bash-interactive-5.3p3/bin:/nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0/bin:/nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev/bin:/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0/bin:/nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1/bin:/nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev/bin:/nix/store/jq2kbdw6ljv9i47jz23pm072cfyxwpfj-postgresql-17.6/bin:/nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0/bin:/nix/store/1p5n2mzy33ayzc1scdnz82h53d192knh-claude-code-1.0.117/bin:/nix/store/8ksax0a2mxglr5hlkj2dzl556jx7xqn5-coreutils-9.7/bin:/nix/store/l964krgbp613d5jxga2vy5qdssj7zfzj-findutils-4.10.0/bin:/nix/store/s2fvny566vls74p4qm9v3fdqd741fh3f-diffutils-3.12/bin:/nix/store/pmhkmqy0vxk47r6ndh0azybhf6gs6k25-gnused-4.9/bin:/nix/store/vlckk0vnmawq9wwh7ndkrwxlpv4h29yh-gnugrep-3.12/bin:/nix/store/03nvbw411p097h6yxjghc33rbcrjfb9d-gawk-5.3.2/bin:/nix/store/8av8pfs7bnyc6hqj764ns4z1fnr9bva1-gnutar-1.35/bin:/nix/store/8gsxxh82rf957ffbsk0q9670nhvl5lia-gzip-1.14/bin:/nix/store/6yjb3zdj448rm8qsmpiq3f67kvj5683a-bzip2-1.0.8-bin/bin:/nix/store/aqdvlkh0jdwkc22hh5vr9sl6qlw5ha74-gnumake-4.4.1/bin:/nix/store/q7sqwn7i6w2b67adw0bmix29pxg85x3w-bash-5.3p3/bin:/nix/store/856i1ajaci3kmmp15rifacfz3jvn5l3q-patch-2.8/bin:/nix/store/y9kgzp85ykrhd7l691w4djx121qygy68-xz-5.8.1-bin/bin:/nix/store/v40ijzz8p2fpk9ihjck3a1ncqaqfmn3c-file-5.45/bin'
export PATH
PS4='+ '
RANLIB='ranlib'
@ -82,7 +82,7 @@ export XDG_DATA_DIRS
__structuredAttrs=''
export __structuredAttrs
_substituteStream_has_warned_replace_deprecation='false'
buildInputs='/nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev /nix/store/008h0z2m22alg2v8kcdcw4v0f7c39lmm-glibc-locales-2.40-66 /nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0 /nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev /nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1 /nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev /nix/store/n4lyyqirbz2j0igs12m2pyqrs7zyyvld-netlify-cli-19.0.2 /nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0'
buildInputs='/nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev /nix/store/008h0z2m22alg2v8kcdcw4v0f7c39lmm-glibc-locales-2.40-66 /nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0 /nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev /nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1 /nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev /nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0 /nix/store/1p5n2mzy33ayzc1scdnz82h53d192knh-claude-code-1.0.117'
export buildInputs
buildPhase='{ echo "------------------------------------------------------------";
echo " WARNING: the existence of this path is not guaranteed.";
@ -161,7 +161,7 @@ declare -a pkgsBuildBuild=()
declare -a pkgsBuildHost=('/nix/store/gx2l0rnp3qcnysdddkg9dqnh2mz6w08k-patchelf-0.15.2' '/nix/store/jwjq0fjgn7d00kswhaw2m8hbgws5vbi4-update-autotools-gnu-config-scripts-hook' '/nix/store/0y5xmdb7qfvimjwbq7ibg1xdgkgjwqng-no-broken-symlinks.sh' '/nix/store/cv1d7p48379km6a85h4zp6kr86brh32q-audit-tmpdir.sh' '/nix/store/85clx3b0xkdf58jn161iy80y5223ilbi-compress-man-pages.sh' '/nix/store/wgrbkkaldkrlrni33ccvm3b6vbxzb656-make-symlinks-relative.sh' '/nix/store/5yzw0vhkyszf2d179m0qfkgxmp5wjjx4-move-docs.sh' '/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh' '/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh' '/nix/store/pag6l61paj1dc9sv15l7bm5c17xn5kyk-move-systemd-user-units.sh' '/nix/store/cmzya9irvxzlkh7lfy6i82gbp0saxqj3-multiple-outputs.sh' '/nix/store/x8c40nfigps493a07sdr2pm5s9j1cdc0-patch-shebangs.sh' '/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh' '/nix/store/xyff06pkhki3qy1ls77w10s0v79c9il0-reproducible-builds.sh' '/nix/store/z7k98578dfzi6l3hsvbivzm7hfqlk0zc-set-source-date-epoch-to-latest.sh' '/nix/store/pilsssjjdxvdphlg2h19p0bfx5q0jzkn-strip.sh' '/nix/store/95k9rsn1zsw1yvir8mj824ldhf90i4qw-gcc-wrapper-14.3.0' '/nix/store/l19cddv64i52rhcwahif8sgyrd3mhiqb-binutils-wrapper-2.44' )
declare -a pkgsBuildTarget=()
declare -a pkgsHostHost=()
declare -a pkgsHostTarget=('/nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev' '/nix/store/ddx7976jyll30xjbasghv9jailswprcp-bash-interactive-5.3p3' '/nix/store/008h0z2m22alg2v8kcdcw4v0f7c39lmm-glibc-locales-2.40-66' '/nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0' '/nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev' '/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0' '/nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1' '/nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev' '/nix/store/msjxcqa4x2f52dyq10rbrbw6k0m0hi90-postgresql-17.6-lib' '/nix/store/jq2kbdw6ljv9i47jz23pm072cfyxwpfj-postgresql-17.6' '/nix/store/n4lyyqirbz2j0igs12m2pyqrs7zyyvld-netlify-cli-19.0.2' '/nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0' )
declare -a pkgsHostTarget=('/nix/store/7zwa3r9agcyzf21d0792fvhrsl6gajiy-bash-interactive-5.3p3-dev' '/nix/store/ddx7976jyll30xjbasghv9jailswprcp-bash-interactive-5.3p3' '/nix/store/008h0z2m22alg2v8kcdcw4v0f7c39lmm-glibc-locales-2.40-66' '/nix/store/q1zaii9cirbfpmwr7d86hpppql3kjcpf-git-2.51.0' '/nix/store/a99hiwhamgzds70gxkfnb4cm8i926356-nodejs-22.19.0-dev' '/nix/store/r4557ald6zn4dzmvgh8na9vwnwzgrjgc-nodejs-22.19.0' '/nix/store/967gn7p1p47ic924r2fx4rgbfp49fhsy-pnpm-10.15.1' '/nix/store/l8m7mbvqxdi9bd5apl8s49kjpnzrcv6c-postgresql-17.6-dev' '/nix/store/msjxcqa4x2f52dyq10rbrbw6k0m0hi90-postgresql-17.6-lib' '/nix/store/jq2kbdw6ljv9i47jz23pm072cfyxwpfj-postgresql-17.6' '/nix/store/ks5kxqrg113jkv9bsvhgpavrq1z1ks4g-inotify-tools-4.23.9.0' '/nix/store/1p5n2mzy33ayzc1scdnz82h53d192knh-claude-code-1.0.117' )
declare -a pkgsTargetTarget=()
declare -a postFixupHooks=('noBrokenSymlinksInAllOutputs' '_makeSymlinksRelativeInAllOutputs' '_multioutPropagateDev' )
declare -a postUnpackHooks=('_updateSourceDateEpochFromSourceRoot' )

4
.gitignore vendored
View File

@ -19,3 +19,7 @@ pnpm-debug.log*
# macOS-specific files
.DS_Store
# Direnv junk
.direnv/*
*/.direnv/*

View File

@ -10,31 +10,25 @@
system:
let
inherit (pkgs.lib) optional optionals;
pkgs = import nixpkgs { inherit system; };
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
in
with pkgs;
{
devShell = pkgs.mkShell {
buildInputs =
[
bashInteractive
glibcLocales
git
nodejs
pnpm
postgresql
netlify-cli
]
++ optional stdenv.isLinux inotify-tools
++ optional stdenv.isDarwin terminal-notifier
++ optionals stdenv.isDarwin (
with darwin.apple_sdk.frameworks;
[
CoreFoundation
CoreServices
]
);
buildInputs = [
bashInteractive
glibcLocales
git
nodejs
pnpm
postgresql
inotify-tools
claude-code
];
};
}
);

14
hindki/.env.example Normal file
View File

@ -0,0 +1,14 @@
# Storage configuration for vocabulary updates
# Options: filesystem (default), git
# For local development (default)
VOCAB_STORAGE_TYPE=filesystem
# For Gitea/GitHub storage in production
# VOCAB_STORAGE_TYPE=git
# GIT_API_URL=https://gitea.example.com # Your Gitea instance URL (omit for GitHub)
# GIT_OWNER=your-username
# GIT_REPO=hindki
# GIT_PATH=src/vocab_list.yaml
# GIT_BRANCH=main
# GIT_TOKEN=your-personal-access-token

View File

@ -0,0 +1,500 @@
import React, { useState, useEffect } from 'react';
interface VocabWord {
english: string;
hindi: string;
gender?: string;
type?: string;
note?: string;
examples?: Array<{
english: string;
hindi: string;
note?: string;
}>;
see_also?: string[];
}
interface VocabCategory {
slug: string;
about: string;
words: VocabWord[];
}
export default function AddVocabForm() {
const [categories, setCategories] = useState<VocabCategory[]>([]);
const [selectedCategory, setSelectedCategory] = useState('');
const [loading, setLoading] = useState(false);
const [categoriesLoading, setCategoriesLoading] = useState(true);
const [message, setMessage] = useState('');
const [showNewCategory, setShowNewCategory] = useState(false);
const [newCategorySlug, setNewCategorySlug] = useState('');
const [newCategoryAbout, setNewCategoryAbout] = useState('');
const [formData, setFormData] = useState<VocabWord>({
english: '',
hindi: '',
gender: '',
type: 'noun',
note: '',
examples: [],
see_also: [],
});
const englishInputRef = React.useRef<HTMLInputElement>(null);
useEffect(() => {
// Get URL parameters
const urlParams = new URLSearchParams(window.location.search);
const catFromUrl = urlParams.get('cat');
const typeFromUrl = urlParams.get('type');
setCategoriesLoading(true);
fetch('/api/vocab.json')
.then(res => {
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
return res.json();
})
.then(data => {
if (Array.isArray(data) && data.length > 0) {
setCategories(data);
// Use category from URL if provided, otherwise first category
if (catFromUrl && data.some(cat => cat.slug === catFromUrl)) {
setSelectedCategory(catFromUrl);
} else if (!selectedCategory && data.length > 0) {
setSelectedCategory(data[0].slug);
}
// Use type from URL if provided
if (typeFromUrl) {
setFormData(prev => ({ ...prev, type: typeFromUrl }));
}
} else {
setMessage('No categories found');
}
})
.catch(err => {
console.error('Failed to load categories:', err);
setMessage(`Failed to load categories: ${err.message}`);
})
.finally(() => {
setCategoriesLoading(false);
});
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setMessage('');
try {
// Build word object with only non-empty fields
const word: any = {
english: formData.english,
hindi: formData.hindi,
type: formData.type,
};
// Only add optional fields if they have values
if (formData.gender) {
word.gender = formData.gender;
}
if (formData.note && formData.note.trim()) {
word.note = formData.note;
}
if (formData.examples && formData.examples.length > 0) {
// Filter out empty examples
const validExamples = formData.examples.filter(
ex => ex.english.trim() || ex.hindi.trim()
);
if (validExamples.length > 0) {
word.examples = validExamples;
}
}
if (formData.see_also && formData.see_also.length > 0) {
// Filter out empty see_also fields
const validSeeAlsos = formData.see_also.filter(
ex => ex.trim()
);
if (validSeeAlsos.length > 0) {
word.see_also = validSeeAlsos;
}
}
// If creating a new category, use the new category details
const categoryToUse = showNewCategory ? newCategorySlug : selectedCategory;
const requestBody: any = {
category: categoryToUse,
word: word,
};
// Include new category information if creating one
if (showNewCategory) {
requestBody.newCategory = {
slug: newCategorySlug,
about: newCategoryAbout,
};
}
const response = await fetch('/api/vocab.json', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
});
const result = await response.json();
if (response.ok) {
setMessage('Word added successfully!');
// Remember the current type and category for bulk entry
const currentType = formData.type;
const currentCategory = showNewCategory ? newCategorySlug : selectedCategory;
// Update URL with current category and type to persist through reloads
const newUrl = `${window.location.pathname}?cat=${currentCategory}&type=${currentType}`;
window.history.replaceState({}, '', newUrl);
// Reset form but keep the type
setFormData({
english: '',
hindi: '',
gender: '',
type: currentType, // Keep the last used type
note: '',
examples: [],
see_also: [],
});
// Focus back on English input for quick bulk entry
setTimeout(() => {
englishInputRef.current?.focus();
}, 100);
// If we created a new category, refresh the categories list
if (showNewCategory) {
setShowNewCategory(false);
setNewCategorySlug('');
setNewCategoryAbout('');
// Refresh categories and set the new category as selected
fetch('/api/vocab.json')
.then(res => res.json())
.then(data => {
if (Array.isArray(data) && data.length > 0) {
setCategories(data);
// Set the newly created category as selected
setSelectedCategory(currentCategory);
}
});
} else {
// For existing categories, just keep it selected
setSelectedCategory(currentCategory);
}
} else {
setMessage(`Error: ${result.error}`);
}
} catch (error) {
setMessage('Failed to add word');
console.error('Error:', error);
} finally {
setLoading(false);
}
};
const addExample = () => {
setFormData({
...formData,
examples: [...(formData.examples || []), { english: '', hindi: '' }],
});
};
const updateExample = (index: number, field: 'english' | 'hindi', value: string) => {
const newExamples = [...(formData.examples || [])];
newExamples[index] = { ...newExamples[index], [field]: value };
setFormData({ ...formData, examples: newExamples });
};
const removeExample = (index: number) => {
const newExamples = (formData.examples || []).filter((_, i) => i !== index);
setFormData({ ...formData, examples: newExamples });
};
// Same thing for see_also
const addSeeAlso = () => {
setFormData({
...formData,
see_also: [...(formData.see_also || []), ''],
});
};
const updateSeeAlso = (index: number, value: string) => {
const newSeeAlso = [...(formData.see_also || [])];
newSeeAlso[index] = value;
setFormData({ ...formData, see_also: newSeeAlso });
}
const removeSeeAlso = (index: number) => {
const newSeeAlso = (formData.see_also || []).filter((_, i) => i !== index);
setFormData({ ...formData, see_also: newSeeAlso });
}
return (
<div className="vocab-form">
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="category">Category:</label>
<select
id="category"
value={showNewCategory ? '__new__' : selectedCategory}
onChange={(e) => {
if (e.target.value === '__new__') {
setShowNewCategory(true);
} else {
setShowNewCategory(false);
setSelectedCategory(e.target.value);
}
}}
required={!showNewCategory}
>
{categories.map((cat) => (
<option key={cat.slug} value={cat.slug}>
{cat.slug.charAt(0).toUpperCase() + cat.slug.slice(1)} - {cat.about}
</option>
))}
<option value="__new__">+ Create New Category</option>
</select>
</div>
{showNewCategory && (
<>
<div className="form-group">
<label htmlFor="newCategorySlug">Category ID (slug):</label>
<input
type="text"
id="newCategorySlug"
value={newCategorySlug}
onChange={(e) => setNewCategorySlug(e.target.value.toLowerCase().replace(/\s+/g, '-'))}
placeholder="e.g., food-and-drink"
required={showNewCategory}
/>
</div>
<div className="form-group">
<label htmlFor="newCategoryAbout">Category Description:</label>
<input
type="text"
id="newCategoryAbout"
value={newCategoryAbout}
onChange={(e) => setNewCategoryAbout(e.target.value)}
placeholder="e.g., Words related to food and beverages"
required={showNewCategory}
/>
</div>
</>
)}
<div className="form-group">
<label htmlFor="english">English:</label>
<input
type="text"
id="english"
ref={englishInputRef}
value={formData.english}
onChange={(e) => setFormData({ ...formData, english: e.target.value })}
autoFocus
required
/>
</div>
<div className="form-group">
<label htmlFor="hindi">Hindi:</label>
<input
type="text"
id="hindi"
value={formData.hindi}
onChange={(e) => setFormData({ ...formData, hindi: e.target.value })}
required
/>
</div>
<div className="form-group">
<label htmlFor="type">Type:</label>
<select
id="type"
value={formData.type}
onChange={(e) => setFormData({ ...formData, type: e.target.value })}
>
<option value="noun">Noun</option>
<option value="verb">Verb</option>
<option value="adjective">Adjective</option>
<option value="adverb">Adverb</option>
<option value="pronoun">Pronoun</option>
<option value="conjunction">Conjunction</option>
<option value="preposition">Preposition</option>
<option value="interjection">Interjection</option>
</select>
</div>
<div className="form-group">
<label htmlFor="gender">Gender (for nouns):</label>
<select
id="gender"
value={formData.gender}
onChange={(e) => setFormData({ ...formData, gender: e.target.value })}
>
<option value="">N/A</option>
<option value="m">Masculine (m)</option>
<option value="f">Feminine (f)</option>
</select>
</div>
<div className="form-group">
<label htmlFor="note">Note:</label>
<textarea
id="note"
value={formData.note}
onChange={(e) => setFormData({ ...formData, note: e.target.value })}
rows={3}
/>
</div>
<div className="form-group">
<label>Examples:</label>
{formData.examples?.map((example, index) => (
<div key={index} className="example-group">
<input
type="text"
placeholder="English example"
value={example.english}
onChange={(e) => updateExample(index, 'english', e.target.value)}
/>
<input
type="text"
placeholder="Hindi example"
value={example.hindi}
onChange={(e) => updateExample(index, 'hindi', e.target.value)}
/>
<button type="button" onClick={() => removeExample(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={addExample}>
Add Example
</button>
</div>
<div className="form-group">
<label>See Also:</label>
{formData.see_also?.map((see_also, index) => (
<div key={index} className="see-also-group">
<input
type="text"
placeholder="Format: [text](link)"
value={see_also}
onChange={(e) => updateSeeAlso(index, e.target.value)}
/>
<button type="button" onClick={() => removeSeeAlso(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={addSeeAlso}>
Add Reference
</button>
</div>
<button type="submit" disabled={loading}>
{loading ? 'Adding...' : 'Add Word'}
</button>
</form>
{message && (
<div className={`message ${message.includes('Error') ? 'error' : 'success'}`}>
{message}
</div>
)}
<style>{`
.vocab-form {
max-width: 600px;
margin: 2rem 0;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.5rem;
border: 1px solid var(--sl-color-gray-5);
border-radius: 4px;
background: var(--sl-color-bg);
color: var(--sl-color-text);
}
.example-group {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.example-group input {
width: auto;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
button[type="button"] {
background: var(--sl-color-gray-6);
margin-right: 0.5rem;
}
.message {
margin-top: 1rem;
padding: 1rem;
border-radius: 4px;
}
.message.success {
background: var(--sl-color-green);
color: white;
}
.message.error {
background: var(--sl-color-red);
color: white;
}
.vocab-form .form-group {
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
}
.vocab-form .form-group label,
.vocab-form .form-group select {
margin: 0;
line-height: 1em;
}
`}</style>
</div>
);
}

View File

@ -27,12 +27,24 @@ const gender_lookup: Record<"m" | "f", ["note" | "tip", string]> = {
"f": ["tip", "feminine"],
};
function renderInline(rawText:string){
// Swap double-dash for em-dash
const text = rawText.replace(/---/g, '&mdash;').replace(/--/g, '&ndash;');
return md.renderInline(text);
}
function render(rawText:string){
// Swap double-dash for em-dash
const text = rawText.replace(/---/g, '&mdash;').replace(/--/g, '&ndash;');
return md.render(text);
}
function highlight(text: string, term: string) {
const terms = term.split(',').map(t => t.trim()).filter(Boolean);
const regex = new RegExp(`(${terms.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})`, 'gi');
const parts = text.split(regex);
return md.renderInline(parts.map((part) => regex.test(part) ? `**${part}**` : part).join(''));
return renderInline(parts.map((part) => regex.test(part) ? `**${part}**` : part).join(''));
}
---
<li id={word.hindi} class="word-entry">
<AnchorHeading level="4" id={word.hindi} class="word-heading" style="font-weight: normal;">
@ -40,7 +52,7 @@ function highlight(text: string, term: string) {
{word.gender && <Badge variant={gender_lookup[word.gender][0]} text={gender_lookup[word.gender][1]} class="gender-badge"/>}
</AnchorHeading>
{word.note &&
<div set:html={md.renderInline(word.note)}></div>
<div set:html={render(word.note)}></div>
}
{
word.examples &&(
@ -65,7 +77,7 @@ word.examples &&(
<p><b>See also:</b>
{ word.see_also.map((ref, i) => (
<>
<span set:html={md.renderInline(ref)}></span>{ i < word.see_also!.length - 1 ? "; " : '' }
<span set:html={renderInline(ref)}></span>{ i < word.see_also!.length - 1 ? "; " : '' }
</>
)) }
</p>

156
hindki/src/lib/storage.ts Normal file
View File

@ -0,0 +1,156 @@
import fs from 'fs/promises';
import path from 'path';
import YAML from 'yaml';
interface StorageAdapter {
read(): Promise<any[]>;
write(data: any[]): Promise<void>;
}
class FileSystemAdapter implements StorageAdapter {
private filePath: string;
constructor(filePath: string) {
this.filePath = filePath;
}
async read(): Promise<any[]> {
const content = await fs.readFile(this.filePath, 'utf-8');
return YAML.parse(content);
}
async write(data: any[]): Promise<void> {
const yaml = YAML.stringify(data);
await fs.writeFile(this.filePath, yaml, 'utf-8');
}
}
class GitAdapter implements StorageAdapter {
private baseUrl: string;
private owner: string;
private repo: string;
private path: string;
private branch: string;
private token: string;
constructor(config: {
baseUrl?: string; // For Gitea, e.g., 'https://gitea.example.com'
owner: string;
repo: string;
path: string;
branch?: string;
token: string;
}) {
this.baseUrl = config.baseUrl || 'https://api.github.com';
this.owner = config.owner;
this.repo = config.repo;
this.path = config.path;
this.branch = config.branch || 'main';
this.token = config.token;
}
private get apiBase(): string {
// Remove trailing slash if present
const base = this.baseUrl.replace(/\/$/, '');
// Add /api/v1 for Gitea if not GitHub
if (!base.includes('api.github.com')) {
return base.includes('/api/v1') ? base : `${base}/api/v1`;
}
return base;
}
async read(): Promise<any[]> {
const response = await fetch(
`${this.apiBase}/repos/${this.owner}/${this.repo}/contents/${this.path}?ref=${this.branch}`,
{
headers: {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`Failed to read from Git: ${response.statusText}`);
}
const data = await response.json();
const content = Buffer.from(data.content, 'base64').toString('utf-8');
return YAML.parse(content);
}
async write(data: any[]): Promise<void> {
// First, get the current file to get its SHA
const currentResponse = await fetch(
`${this.apiBase}/repos/${this.owner}/${this.repo}/contents/${this.path}?ref=${this.branch}`,
{
headers: {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/json',
},
}
);
if (!currentResponse.ok) {
throw new Error(`Failed to get current file from Git: ${currentResponse.statusText}`);
}
const currentData = await currentResponse.json();
const sha = currentData.sha;
// Now update the file
const yaml = YAML.stringify(data);
const content = Buffer.from(yaml).toString('base64');
const updateResponse = await fetch(
`${this.apiBase}/repos/${this.owner}/${this.repo}/contents/${this.path}`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${this.token}`,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Update vocab list via web form',
content: content,
sha: sha,
branch: this.branch,
}),
}
);
if (!updateResponse.ok) {
const errorText = await updateResponse.text();
throw new Error(`Failed to update Git file: ${updateResponse.statusText} - ${errorText}`);
}
}
}
export function getStorageAdapter(): StorageAdapter {
const storageType = import.meta.env.VOCAB_STORAGE_TYPE || 'filesystem';
switch (storageType) {
case 'git':
case 'github':
case 'gitea':
if (!import.meta.env.GIT_TOKEN) {
throw new Error('GIT_TOKEN environment variable is required for Git storage');
}
return new GitAdapter({
baseUrl: import.meta.env.GIT_API_URL, // e.g., 'https://gitea.example.com' for Gitea
owner: import.meta.env.GIT_OWNER || 'your-username',
repo: import.meta.env.GIT_REPO || 'your-repo',
path: import.meta.env.GIT_PATH || 'src/vocab_list.yaml',
branch: import.meta.env.GIT_BRANCH || 'main',
token: import.meta.env.GIT_TOKEN,
});
case 'filesystem':
default:
const filePath = path.join(process.cwd(), 'src', 'vocab_list.yaml');
return new FileSystemAdapter(filePath);
}
}
export { StorageAdapter, FileSystemAdapter, GitAdapter };

View File

@ -0,0 +1,108 @@
import type { APIRoute } from 'astro';
import { getStorageAdapter } from '../../lib/storage';
export const prerender = false;
export const GET: APIRoute = async () => {
try {
const storage = getStorageAdapter();
const data = await storage.read();
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.error('Error in GET /api/vocab:', error);
return new Response(JSON.stringify({ error: 'Failed to read vocab list' }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
};
export const POST: APIRoute = async ({ request }) => {
try {
const data = await request.json();
const storage = getStorageAdapter();
// Read existing data
const vocabList = await storage.read();
// Validate the new entry
if (!data.category || !data.word) {
return new Response(JSON.stringify({ error: 'Missing required fields' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}
// Check if we're creating a new category
if (data.newCategory) {
// Validate new category data
if (!data.newCategory.slug || !data.newCategory.about) {
return new Response(JSON.stringify({ error: 'New category requires slug and description' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}
// Check if category already exists
const existingCategory = vocabList.find((cat: any) => cat.slug === data.newCategory.slug);
if (existingCategory) {
return new Response(JSON.stringify({ error: 'Category already exists' }), {
status: 400,
headers: {
'Content-Type': 'application/json',
},
});
}
// Create the new category with the first word
vocabList.push({
slug: data.newCategory.slug,
about: data.newCategory.about,
words: [data.word],
});
} else {
// Find the existing category to add to
const categoryIndex = vocabList.findIndex((cat: any) => cat.slug === data.category);
if (categoryIndex === -1) {
return new Response(JSON.stringify({ error: 'Category not found' }), {
status: 404,
headers: {
'Content-Type': 'application/json',
},
});
}
// Add the new word to the existing category
vocabList[categoryIndex].words.push(data.word);
}
// Write back using the storage adapter
await storage.write(vocabList);
return new Response(JSON.stringify({ success: true, message: 'Word added successfully' }), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
} catch (error) {
console.error('Error in POST /api/vocab:', error);
return new Response(JSON.stringify({ error: 'Failed to update vocab list' }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
};

View File

@ -1,8 +1,6 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import fs from "fs";
const raw_yaml = fs.readFileSync("src/vocab_list.yaml", "utf-8");
import AddVocabForm from "../../components/AddVocabForm.tsx";
---
<StarlightPage
@ -12,17 +10,5 @@ const raw_yaml = fs.readFileSync("src/vocab_list.yaml", "utf-8");
prev: false,
}}
>
{raw_yaml}
<AddVocabForm client:only="react" />
</StarlightPage>
<style>
:global(div.sl-container) {
margin-inline: 0 !important;
}
:global([yaml-editor]) {
width: calc(
100vw - var(--sl-sidebar-width) - 2 * var(--sl-content-pad-x)
);
}
</style>

View File

@ -3,6 +3,10 @@
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import AnchorHeading from '@astrojs/starlight/components/AnchorHeading.astro';
import VocabWord from '@/components/VocabWord.astro';
import markdownit from 'markdown-it'
import markdownItMark from 'markdown-it-mark'
const md = markdownit().use(markdownItMark);
function titlecase(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
@ -43,9 +47,7 @@ const headings = wordsByType.map(({type, words}) => {
frontmatter={{ title: "Vocabulary: " + (entry?.id ?? "Uncategorized") }}
headings={headings}
>
<div>
{ entry?.data.about}
</div>
<div set:html={md.render(entry!.data.about!)}/>
{
wordsByType.map(({type, words}) => (
<div class="word-type-section">

View File

@ -282,6 +282,7 @@ mark {
max-width: 100%;
}
/* =================================================================
LANGUAGE-SPECIFIC STYLES
================================================================= */

View File

@ -1 +0,0 @@
/home/ryan/Documents/Code/hindi-server/hindi_server/vocab_list.yaml

325
hindki/src/vocab_list.yaml Normal file
View File

@ -0,0 +1,325 @@
- about: Common everyday vocabulary words.
slug: general
words:
- english: thing
examples:
- english: "No problem. *(Literally: [there is] nothing.)*"
hindi: कोई बात नहीं.
- english: I did not understand this thing (matter).
hindi: मुझे यह बात [समझ](#understand) नहीं आई.
note: >
आई, feminine, matches बात because the postposition in मुझे blocks
the gender matching on मैं (Recall [मुझे = मैं +
की](/grammar/pronouns)).
- english: "to talk (seems to be more like: *to chat*)."
hindi: बात करना
gender: f
hindi: बात
note: More abstract than "[चीज़](#चीज़)", often used for matters or topics -- as
in, things that are discussed.
see_also:
- "[चीज़](#चीज़)"
type: noun
- english: help
examples:
- english: Please help me.
hindi: मेरी मदद किजिए.
- english: Have you been helped?
hindi: क्या आपकी मदद हुई है?
gender: f
hindi: मदद
note: Used with का/के/की to indicate who is receiving help, and with either करना
(to actively help someone), or होना (to passively be helped). See the
links below for the generalized rules of these.
see_also:
- "[Reflexive verbs](/grammar/reflexive-verbs)"
- "[Active and passive, transitive and
intransitive](/grammar/active-passive-transitive-intransitive)"
type: noun
- english: message
hindi: संदेश
type: noun
gender: m
- about: Hindi often uses repetition of words for emphasis or to indicate a
variety of related meanings.
slug: repetition
words:
- english: what all, which all
examples:
- english: What all did you do last week?
hindi: पिछले हफ्ते तुमने क्या-क्या किया?
hindi: क्या-क्या
type: noun
- hindi: धीरे-धीरे
english: slowly, gradually
examples:
- english: Please speak slowly.
hindi: कृपया धीरे-धीरे बोलिए.
type: adverb
- about: Words for people from different countries.
slug: nationalities
words:
- english: Afghan
hindi: अफ़्गानी
type: adjective
- english: African
hindi: अफ़्रीकी
type: adjective
- english: American
hindi: अमरीकी
type: adjective
- english: Asian
hindi: एशियाई
type: adjective
- english: Australian
hindi: ओस्ट्रेलियन
type: adjective
- english: Bangladeshi
hindi: बंगला, बंगलादेशी
type: adjective
- english: Bhutanese
hindi: भूटानी
type: adjective
- english: British/English
hindi: ब्रितानी/अँग्रेज़
type: adjective
- english: Chinese
hindi: चीनी
type: adjective
- english: French
hindi: फ़्रांसीसी
type: adjective
- english: German
hindi: जर्मन
type: adjective
- english: Greek
hindi: यूनानी
type: adjective
- english: Indian
hindi: भारतीय, हिंदुस्तानी
note: There is some cultural context here, as obviously [not everyone in India
is Hindu](/culture/bharat-hindustan).
type: adjective
- english: Iranian
hindi: ईरानी
type: adjective
- english: Iraqi
hindi: ईराकी
type: adjective
- english: Israeli
hindi: इज़राईली
type: adjective
- english: Italian
hindi: इतालवी
type: adjective
- english: Japanese
hindi: जापानी
type: adjective
- english: Nepalese
hindi: नेपाली
type: adjective
- english: Pakistani
hindi: पाकिस्तानी
type: adjective
- english: Russian
hindi: रूसी
type: adjective
- english: Vietnamese
hindi: वियतनामी
type: adjective
- slug: vocations
about: >-
Common occupations. Almost all of these are unmarked masculine nouns, except
the few that have modified forms for females. I assume, though I should
definitely confirm such a basic thing, that the words can be applied as-is
for females too (e.g., मेरा नर्स / मेरी नर्स both work, based on if your
nurse is male or female).
It seems like this is one of those cases of [ingrained
sexism](/culture/sexism) you often find in Hindi.
words:
- english: accountant
hindi: मुनीम
type: noun
- english: actor, actress
hindi: अभिनेता, अभिनेत्री
type: noun
- english: ambassador
hindi: राजदूत
type: noun
- english: artisan
hindi: कारीगर
type: noun
- english: artist
hindi: कलाकार
type: noun
- english: bank teller
hindi: खज़ांची
type: noun
- english: businessman/businesswoman
hindi: व्यवसायी
type: noun
- english: carpenter
hindi: बढ़ई
type: noun
- english: confectioner
hindi: हलवाई
type: noun
- english: dancer
hindi: नर्तकी
gender: f
type: noun
note: There's also नर्तक, the masculine form.
- english: diplomat
hindi: राजनायक
type: noun
- english: doctor
hindi: चिकित्सक, डाक्टर
type: noun
- english: driver
hindi: चालक, ड्राइवर
type: noun
- english: engineer
hindi: अभियंता
type: noun
- english: farmer
hindi: किसान, कृषक
type: noun
- english: gardener
hindi: माली
type: noun
- english: hairdresser
hindi: नाई
type: noun
- english: jeweler
hindi: जौहरी
type: noun
note: In addition to being a cognate (in my opinion at least), this was the name
of a favorite hotel ("The Johri") in Jaipur's Johri Bazaar (Jeweller's
Market).
- english: judge
hindi: न्यायधीश
type: noun
- english: laborer
hindi: मज़दूर
type: noun
note: I often confuse this word with मज़बूर (forced), which seems to have
obvious etymological overlap.
- english: lawyer
hindi: वक़ील
type: noun
- english: mailman
hindi: डाकिया
type: noun
- english: mechanic
hindi: मिस्त्री
type: noun
- english: merchant
hindi: व्यापारी
type: noun
note: In need of a good way to differentiate this from व्यवसायी.
- english: musician
hindi: संगीतकार
type: noun
- english: nurse
hindi: नर्स
type: noun
note: It's just straight-up the English word.
- english: athlete/player
hindi: खिलाड़ी
type: noun
see_also:
- "[खेलना](/vocabulary/general#खेलना), to play"
- english: police officer
hindi: पुलिस अधिकारी
type: noun
note: >-
The first word is obviously just "police", but I thought it was
interesting that buried in the second word is "धिकार" -- "shame" --
[prefixed with "अ-"](/grammar/prefixes), as in "without" (in the same
way that, say, "amoral" is the opposite of "moral" in English).
I'm not saying the etymology is implying police are without shame: taken
as a whole, "अधिकार" just means "authority."
- english: potter
hindi: कुम्हार
type: noun
note: Interesting that "Kumar" is a common surname in Hindi, as "Potter" is
fairly common in English.
- english: president
hindi: राष्ट्रपति
type: noun
note: 'Literally: "Nation Husband". Weird, but I guess kind of makes sense?
Would they change this if India ever had a female president? (Could
India ever?)'
- english: prime minister
hindi: प्रधानमंत्री
type: noun
note: "As you'd hope: प्रधान means \"prime/head/chief\", मंत्री means
\"minister.\""
- english: publisher
hindi: प्रकाशक
type: noun
- english: salesman
hindi: विक्रेता
type: noun
- english: sculptor
hindi: मूर्तिकार
type: noun
- english: security guard
hindi: सुरक्षा कर्मचारी
type: noun
note: I put the translation as "security guard", but while सुरक्षा does mean
"security", कर्मचारी just means "employee."
- english: shopkeeper
hindi: दुकानदार
type: noun
- english: singer
hindi: गायिका
type: noun
gender: f
note: >-
Like नर्तकी / नर्तक, there is also गायक for the masculine form.
Take note of the difference between this word, which has गाना -- to sing
-- as its root, and संगीतकार, which has संगीत -- music -- as its root.
- english: student
hindi: विद्यार्थी
type: noun
note: >-
विद्या = knowledge, अर्थ = meaning.
Feels kind of redundant (I would have expected the compound word to be,
say, "knowledge + seeker" or something) but unless I'm missing something
... there you have it.
- english: tailor
hindi: दर्ज़ी
type: noun
- english: teacher
hindi: अध्यापिका
type: noun
gender: f
note: अध्यापक for the masculine form.
- english: thief
hindi: चोर
type: noun
note: Mumbai has a चोर बाज़ार, "thief's market."
- english: tourist
hindi: पर्यटक
type: noun
- english: washerman
hindi: धोबी
type: noun
note: From धोना, to wash.
- english: writer
hindi: लेखिका
type: noun
gender: f
note: |-
Again, लेखक for masculine form.
From लिखना, to write.