urdu-language-support
2
总安装量
2
周安装量
#65618
全站排名
安装命令
npx skills add https://github.com/maneeshanif/todo-spec-driven --skill urdu-language-support
Agent 安装分布
opencode
2
gemini-cli
2
claude-code
2
github-copilot
2
codex
2
kimi-cli
2
Skill 文档
Urdu Language Support Skill
Quick Start
- Read Phase 5 Constitution –
constitution-prompt-phase-5.md - Set up i18n – next-intl or react-intl
- Create Urdu translations – JSON translation files
- Configure RTL support – Tailwind RTL plugin
- Update AI agent – Urdu response capability
- Add language switcher – UI component
Bonus Points: +100
This is a bonus feature for Phase 5 that adds 100 points to the hackathon score.
i18n Setup with next-intl
Installation
cd frontend
npm install next-intl
Next.js Configuration
// frontend/next.config.ts
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
const nextConfig = {
// ... other config
};
export default withNextIntl(nextConfig);
i18n Configuration
// frontend/i18n.ts
import { getRequestConfig } from 'next-intl/server';
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`./messages/${locale}.json`)).default
}));
Middleware for Locale Detection
// frontend/middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'ur'],
defaultLocale: 'en',
localePrefix: 'as-needed'
});
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)']
};
Translation Files
English Translations
// frontend/messages/en.json
{
"common": {
"appName": "Evolution Todo",
"loading": "Loading...",
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"search": "Search",
"filter": "Filter",
"sort": "Sort"
},
"auth": {
"login": "Login",
"signup": "Sign Up",
"logout": "Logout",
"email": "Email",
"password": "Password",
"forgotPassword": "Forgot Password?"
},
"tasks": {
"title": "Tasks",
"newTask": "New Task",
"addTask": "Add Task",
"editTask": "Edit Task",
"deleteTask": "Delete Task",
"taskTitle": "Task Title",
"description": "Description",
"priority": "Priority",
"dueDate": "Due Date",
"status": "Status",
"tags": "Tags",
"reminder": "Reminder",
"priorities": {
"low": "Low",
"medium": "Medium",
"high": "High"
},
"statuses": {
"pending": "Pending",
"inProgress": "In Progress",
"completed": "Completed"
},
"noTasks": "No tasks found",
"completedTasks": "Completed Tasks"
},
"chat": {
"title": "AI Assistant",
"placeholder": "Ask me anything about your tasks...",
"send": "Send",
"thinking": "Thinking...",
"newConversation": "New Conversation"
},
"settings": {
"title": "Settings",
"language": "Language",
"theme": "Theme",
"notifications": "Notifications"
}
}
Urdu Translations
// frontend/messages/ur.json
{
"common": {
"appName": "اÛÙÙÙÛÙØ´Ù Ù¹ÙÚÙ",
"loading": "ÙÙÚ ÛÙ Ø±ÛØ§ ÛÛ...",
"save": "Ù
ØÙÙØ¸ کرÛÚº",
"cancel": "Ù
ÙØ³ÙØ® کرÛÚº",
"delete": "ØØ°Ù کرÛÚº",
"edit": "ترÙ
ÛÙ
کرÛÚº",
"search": "ØªÙØ§Ø´ کرÛÚº",
"filter": "ÙÙٹر",
"sort": "ØªØ±ØªÛØ¨ دÛÚº"
},
"auth": {
"login": "ÙØ§Ú¯ اÙ",
"signup": "سائ٠اپ",
"logout": "ÙØ§Ú¯ آؤٹ",
"email": "Ø§Û Ù
ÛÙ",
"password": "پاس ÙØ±Ú",
"forgotPassword": "پاس ÙØ±Ú بھÙÙ Ú¯Ø¦ÛØ"
},
"tasks": {
"title": "کاÙ
",
"newTask": "ÙÛØ§ کاÙ
",
"addTask": "کاÙ
شاÙ
٠کرÛÚº",
"editTask": "کاÙ
Ù
ÛÚº ترÙ
ÛÙ
کرÛÚº",
"deleteTask": "کاÙ
ØØ°Ù کرÛÚº",
"taskTitle": "کاÙ
کا عÙÙØ§Ù",
"description": "ØªÙØµÛÙ",
"priority": "ØªØ±Ø¬ÛØ",
"dueDate": "Ø¢Ø®Ø±Û ØªØ§Ø±ÛØ®",
"status": "ØÛØ«ÛØª",
"tags": "Ù¹Ûگز",
"reminder": "ÛØ§Ø¯ Ø¯ÛØ§ÙÛ",
"priorities": {
"low": "Ú©Ù
",
"medium": "درÙ
ÛØ§ÙÛ",
"high": "Ø²ÛØ§Ø¯Û"
},
"statuses": {
"pending": "Ø²ÛØ± Ø§ÙØªÙاء",
"inProgress": "جارÛ",
"completed": "Ù
Ú©Ù
Ù"
},
"noTasks": "Ú©ÙØ¦Û کاÙ
ÙÛÛÚº Ù
ÙØ§",
"completedTasks": "Ù
Ú©Ù
Ù Ø´Ø¯Û Ú©Ø§Ù
"
},
"chat": {
"title": "AI Ù
عاÙÙ",
"placeholder": "اپÙÛ Ú©Ø§Ù
ÙÚº Ú©Û Ø¨Ø§Ø±Û Ù
ÛÚº Ú©ÚÚ¾ Ø¨Ú¾Û Ù¾ÙÚÚ¾ÛÚº...",
"send": "Ø¨Ú¾ÛØ¬ÛÚº",
"thinking": "سÙÚ Ø±ÛØ§ ÛÙÚº...",
"newConversation": "ÙØ¦Û Ú¯ÙØªÚ¯Ù"
},
"settings": {
"title": "ØªØ±ØªÛØ¨Ø§Øª",
"language": "زباÙ",
"theme": "تھÛÙ
",
"notifications": "Ø§Ø·ÙØ§Ø¹Ø§Øª"
}
}
RTL Support with Tailwind
Tailwind RTL Plugin
npm install tailwindcss-rtl
Tailwind Configuration
// frontend/tailwind.config.ts
import type { Config } from "tailwindcss";
import rtl from "tailwindcss-rtl";
const config: Config = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [rtl],
};
export default config;
RTL Layout Component
// frontend/components/layout/rtl-provider.tsx
"use client";
import { useLocale } from "next-intl";
import { useEffect } from "react";
export function RTLProvider({ children }: { children: React.ReactNode }) {
const locale = useLocale();
const isRTL = locale === "ur";
useEffect(() => {
document.documentElement.dir = isRTL ? "rtl" : "ltr";
document.documentElement.lang = locale;
}, [locale, isRTL]);
return <>{children}</>;
}
RTL-Aware Components
// frontend/components/ui/rtl-text.tsx
import { cn } from "@/lib/utils";
import { useLocale } from "next-intl";
interface RTLTextProps {
children: React.ReactNode;
className?: string;
}
export function RTLText({ children, className }: RTLTextProps) {
const locale = useLocale();
const isRTL = locale === "ur";
return (
<span className={cn(
isRTL && "font-urdu", // Custom Urdu font
className
)}>
{children}
</span>
);
}
Language Switcher Component
// frontend/components/language-switcher.tsx
"use client";
import { useLocale, useTranslations } from "next-intl";
import { useRouter, usePathname } from "next/navigation";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Globe } from "lucide-react";
const languages = [
{ code: "en", name: "English", nativeName: "English" },
{ code: "ur", name: "Urdu", nativeName: "اردÙ" },
];
export function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const t = useTranslations("settings");
const handleChange = (newLocale: string) => {
// Remove current locale prefix and add new one
const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
router.push(newPath);
};
return (
<Select value={locale} onValueChange={handleChange}>
<SelectTrigger className="w-[140px]">
<Globe className="h-4 w-4 me-2" />
<SelectValue />
</SelectTrigger>
<SelectContent>
{languages.map((lang) => (
<SelectItem key={lang.code} value={lang.code}>
{lang.nativeName}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
AI Agent Urdu Support
Update Agent Prompt
# backend/src/agents/prompts.py
SYSTEM_PROMPT_URDU = """
آپ اÛÚ© Ù
ددگار AI اسسٹÙÙ¹ ÛÛÚº ج٠صارÙÛÙ Ú©Ù Ø§Ù Ú©Û Ú©Ø§Ù
ÙÚº کا Ø§ÙØªØ¸Ø§Ù
کرÙÛ Ù
ÛÚº Ù
دد کرتا ÛÛÛ
آپ ÛÛ Ú©Ø± Ø³Ú©ØªÛ ÛÛÚº:
- ÙØ¦Û کاÙ
Ø¨ÙØ§Ø¦ÛÚº
- کاÙ
ÙÚº Ú©Û ÙÛØ±Ø³Øª دÛÚ©Ú¾ÛÚº
- کاÙ
Ù
Ú©Ù
٠کرÛÚº
- کاÙ
ØØ°Ù کرÛÚº
- کاÙ
ÙÚº Ù
ÛÚº ترÙ
ÛÙ
کرÛÚº
- ÛØ§Ø¯ Ø¯ÛØ§ÙÛØ§Úº Ù
ÙØ±Ø± کرÛÚº
ÛÙ
ÛØ´Û ارد٠Ù
ÛÚº Ø¬ÙØ§Ø¨ دÛÚº Ø§ÙØ± Ø´Ø§Ø¦Ø³ØªÛ Ø±ÛÛÚºÛ
"""
def get_system_prompt(language: str = "en") -> str:
"""Get system prompt based on language."""
if language == "ur":
return SYSTEM_PROMPT_URDU
return SYSTEM_PROMPT_EN
Language Detection
# backend/src/agents/language.py
import langdetect
def detect_language(text: str) -> str:
"""Detect language of input text."""
try:
lang = langdetect.detect(text)
return "ur" if lang == "ur" else "en"
except:
return "en"
def should_respond_in_urdu(user_message: str) -> bool:
"""Check if response should be in Urdu."""
return detect_language(user_message) == "ur"
Updated Agent
# backend/src/agents/todo_agent.py
from .language import should_respond_in_urdu, get_system_prompt
async def run_agent(user_message: str, user_id: str):
"""Run AI agent with language support."""
# Detect language
use_urdu = should_respond_in_urdu(user_message)
system_prompt = get_system_prompt("ur" if use_urdu else "en")
# Create agent with appropriate prompt
agent = Agent(
name="todo_assistant",
instructions=system_prompt,
model="gemini/gemini-2.0-flash",
tools=[...],
)
# Run and return response
response = await Runner.run(agent, user_message)
return response
Urdu Font Configuration
Google Fonts Setup
// frontend/app/layout.tsx
import { Noto_Nastaliq_Urdu, Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const urduFont = Noto_Nastaliq_Urdu({
subsets: ["arabic"],
variable: "--font-urdu",
weight: ["400", "700"],
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body className={`${inter.variable} ${urduFont.variable}`}>
{children}
</body>
</html>
);
}
Tailwind Font Configuration
// frontend/tailwind.config.ts
const config: Config = {
theme: {
extend: {
fontFamily: {
sans: ["var(--font-inter)", "sans-serif"],
urdu: ["var(--font-urdu)", "serif"],
},
},
},
};
Usage in Components
// frontend/components/tasks/task-card.tsx
"use client";
import { useTranslations } from "next-intl";
import { PriorityBadge } from "./priority-badge";
export function TaskCard({ task }: { task: Task }) {
const t = useTranslations("tasks");
return (
<div className="p-4 border rounded-lg">
<h3 className="font-bold">{task.title}</h3>
<p className="text-muted-foreground">{task.description}</p>
<div className="flex items-center gap-2 mt-2">
<span className="text-sm">{t("priority")}:</span>
<PriorityBadge priority={task.priority} />
</div>
<div className="flex items-center gap-2 mt-1">
<span className="text-sm">{t("status")}:</span>
<span>{t(`statuses.${task.status}`)}</span>
</div>
</div>
);
}
Verification Checklist
- next-intl installed and configured
- English translations complete
- Urdu translations complete
- RTL support working
- Language switcher component created
- Urdu font loaded (Noto Nastaliq)
- AI agent responds in Urdu
- All UI text is translatable
- Direction changes correctly on language switch
- No layout breaks in RTL mode
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Font not loading | Missing import | Add Noto Nastaliq to layout |
| RTL not working | Missing dir attribute | Set in RTLProvider |
| Translation missing | Key not in JSON | Add to messages file |
| AI responds in English | No language detection | Add langdetect check |
| Text overflow | RTL text handling | Use text-start/text-end |