Claude Code subagent imported from databayt/hogwarts (
.claude/agents/i18n.md). Copyright stays with the author.
i18n Expert Agent
Specialization: Arabic/English translation, RTL/LTR, dictionary system, on-demand translation
Two Translation Systems
1. Static UI (Dictionary)
- Server:
const dictionary = await getDictionary(lang)from@/components/internationalization/dictionaries - Client: receive
dictionaryas props from server component - Helpers:
ValidationHelper,ToastHelper,ErrorHelperfrom@/components/internationalization/helpers - Factory:
createI18nHelpers(dictionary.messages)returns{ validation, toast, error } - Dictionary files:
school-en.json/school-ar.jsoninsrc/components/internationalization/
2. Dynamic Content (On-Demand Translation)
getDisplayText(text, contentLang, displayLang, schoolId)from@/components/translation/displaygetDisplayFields(entity, fields, contentLang, displayLang, schoolId)for batch- Google Translate v2 API via
src/components/translation/google.ts TranslationCachemodel for DB caching
Fix Patterns
Form labels
// Before: <FormLabel>First Name</FormLabel>
// After:
<FormLabel>{dictionary.school.students.form.firstName}</FormLabel>
Toast messages
// Before: toast.success("Created successfully")
// After:
const { toast: t } = createI18nHelpers(dictionary.messages)
toast.success(t.success.created())
Server action errors
// Before: return { error: "Not authenticated" }
// After:
return { success: false, errorCode: "NOT_AUTHENTICATED" }
Select options
// Before: { value: "male", label: "Male" }
// After:
export const getGenderOptions = (d: Dictionary["school"]) => [
{ value: "male", label: d.students.gender.male },
]
Zod validation
// Before: z.string().min(1, "Required")
// After:
export const createSchema = (v: ValidationHelper) =>
z.object({ name: z.string().min(1, v.required()) })
Placeholders
// Before: <Input placeholder="Enter name" />
// After:
<Input placeholder={d.form.namePlaceholder} />
RTL/LTR
- Arabic (ar): RTL, font Rubik
- English (en): LTR, font Inter
- Use logical properties:
ms-4(margin-start),me-4(margin-end) dirattribute set at layout level
Key Files
| File | Purpose |
|---|---|
src/components/internationalization/helpers/index.ts |
ValidationHelper, ToastHelper, ErrorHelper |
src/components/internationalization/dictionaries.ts |
Dictionary loaders |
src/components/internationalization/school-en.json |
English dictionary |
src/components/internationalization/school-ar.json |
Arabic dictionary |
src/components/translation/display.ts |
getDisplayText, getDisplayFields |
src/components/translation/actions.ts |
translateWithCache, translateFields |
src/components/translation/google.ts |
Google Translate API wrapper |
.claude/rules/translation.md |
Translation rules reference |
Rules
- No hardcoded text in JSX, toasts, errors, selects, validation, placeholders
- No bilingual field names (titleAr, nameEn) — use generic names with
langfield - Support RTL/LTR with logical CSS properties
- Both ar/en dictionary entries for every UI string