Skip to content

Commit bb1bd39

Browse files
refactor(i18n): implement dynamic language file loading
1 parent f46c6d9 commit bb1bd39

File tree

3 files changed

+199
-19
lines changed

3 files changed

+199
-19
lines changed

CONTRIBUTING.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Contributing to asknot-rocky
2+
3+
Thank you for your interest in contributing to asknot-rocky! This guide will help you get started with contributing to the project.
4+
5+
## Table of Contents
6+
7+
- [Code of Conduct](#code-of-conduct)
8+
- [Getting Started](#getting-started)
9+
- [Development Setup](#development-setup)
10+
- [Making Changes](#making-changes)
11+
- [Submitting Changes](#submitting-changes)
12+
- [Code Style](#code-style)
13+
- [Testing](#testing)
14+
- [Project Structure](#project-structure)
15+
- [Contributing Translations](#contributing-translations)
16+
17+
## Code of Conduct
18+
19+
By participating in this project, you are expected to uphold our Code of Conduct, which promotes a respectful and inclusive environment for all contributors.
20+
21+
## Getting Started
22+
23+
1. Fork the repository on GitHub
24+
2. Clone your fork locally:
25+
```bash
26+
git clone https://github.com/YOUR-USERNAME/asknot-rocky.git
27+
cd asknot-rocky
28+
```
29+
3. Add the upstream repository as a remote:
30+
```bash
31+
git remote add upstream https://github.com/rocky-linux/asknot-rocky.git
32+
```
33+
34+
## Development Setup
35+
36+
1. Ensure you have Node.js installed (latest LTS version recommended)
37+
2. Install dependencies:
38+
```bash
39+
npm install
40+
```
41+
3. Start the development server:
42+
```bash
43+
npm run dev
44+
```
45+
4. Visit `http://localhost:4321` in your browser
46+
47+
## Making Changes
48+
49+
1. Create a new branch for your changes:
50+
```bash
51+
git checkout -b feature/your-feature-name
52+
```
53+
2. Make your changes
54+
3. Ensure your changes meet our code quality standards:
55+
```bash
56+
npm run lint # Run ESLint
57+
npm run format # Run Prettier
58+
npm run typecheck # Run TypeScript checks
59+
npm run test # Run tests
60+
```
61+
62+
## Submitting Changes
63+
64+
1. Commit your changes with a clear and descriptive commit message
65+
2. Push to your fork:
66+
```bash
67+
git push origin feature/your-feature-name
68+
```
69+
3. Create a Pull Request from your fork to our main repository
70+
4. Ensure all CI checks pass
71+
5. Wait for review from maintainers
72+
73+
## Code Style
74+
75+
We use several tools to maintain code quality:
76+
77+
- ESLint for code linting
78+
- Prettier for code formatting
79+
- TypeScript for type checking
80+
81+
Our automated workflows will check these when you submit a PR. You can run them locally:
82+
83+
```bash
84+
npm run lint:fix # Fix linting issues
85+
npm run format # Format code
86+
npm run typecheck # Check types
87+
```
88+
89+
## Testing
90+
91+
We use Vitest for testing. Write tests for new features and ensure existing tests pass:
92+
93+
```bash
94+
npm run test # Run tests
95+
npm run test:coverage # Run tests with coverage report
96+
```
97+
98+
## Project Structure
99+
100+
```
101+
asknot-rocky/
102+
├── src/ # Source files
103+
│ ├── components/ # React components (Button, QuestionNode, etc.)
104+
│ ├── i18n/ # Internationalization files and language support
105+
│ ├── layouts/ # Page layouts
106+
│ ├── pages/ # Astro pages
107+
│ ├── questions/ # Question tree logic and configuration
108+
│ ├── styles/ # Global styles
109+
│ └── test/ # Test utilities and shared test code
110+
├── public/ # Static assets
111+
```
112+
113+
Key files:
114+
115+
- `src/pages/index.astro` – Main application page
116+
- `src/components/QuestionNode.tsx` – Core question navigation component
117+
- `src/questions/questions.ts` – Question tree configuration and types
118+
- `src/i18n/LanguageContext.tsx` – Internationalization context and logic
119+
- `src/i18n/{langCode}.json` – Translations
120+
121+
## Contributing Translations
122+
123+
We welcome contributions to make asknot-rocky accessible in more languages! The project currently supports English (en) and Spanish (es), with room for more languages.
124+
125+
### Translation Structure
126+
127+
Translations are stored in JSON files in the `src/i18n` directory, with each language having its own file (e.g., `en.json`, `es.json`). The translation files follow a specific structure defined in `src/i18n/types.ts`.
128+
129+
Key areas that need translation include:
130+
131+
- Home page content
132+
- Question categories and options
133+
- Error messages
134+
- UI elements (footer, language selector)
135+
136+
### Adding a New Language
137+
138+
1. Create a new JSON file in `src/i18n` named with your language code (e.g., `fr.json` for French)
139+
2. Copy the structure from `en.json` as a template
140+
3. Add your language code and name to the `LANGUAGE_NAMES` object in `src/i18n/types.ts`:
141+
```typescript
142+
export const LANGUAGE_NAMES: Record<LanguageCode, string> = {
143+
en: 'English',
144+
es: 'Español',
145+
fr: 'Français', // Your new language
146+
} as const;
147+
```
148+
4. Update the `LanguageCode` type in `src/i18n/types.ts`:
149+
```typescript
150+
export type LanguageCode = 'en' | 'es' | 'fr'; // Add your language code
151+
```
152+
5. Translate all strings in your new language file
153+
154+
The language will be automatically loaded and available in the language selector once you've completed these steps. The application uses Vite's dynamic imports to automatically discover and load all language files in the `src/i18n` directory.
155+
156+
### Translation Guidelines
157+
158+
1. Maintain the same JSON structure as the English version
159+
2. Keep special placeholders and HTML tags intact
160+
3. Ensure all keys are present and match exactly
161+
4. Test the UI with your translations to verify layout and formatting
162+
5. Include regional variations only if necessary (e.g., `pt-BR` vs `pt-PT`)
163+
164+
### Testing Translations
165+
166+
After adding or modifying translations:
167+
168+
1. Run `npm run typecheck` to ensure type safety
169+
2. Start the development server and test the UI in your language
170+
3. Verify all interactive elements work correctly
171+
4. Check text formatting and layout in different screen sizes
172+
173+
## Need Help?
174+
175+
If you need help or have questions, please:
176+
177+
1. Check existing issues on GitHub
178+
2. Create a new issue if needed
179+
3. Join the Rocky Linux community channels
180+
181+
Thank you for contributing to asknot-rocky!

src/components/LanguageSelector.tsx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,11 @@ import React from 'react';
99

1010
import { useLanguage } from '@/i18n/LanguageContext';
1111

12-
/**
13-
* Available languages in the application
14-
* Add new languages here to support additional translations
15-
*/
16-
const languages = {
17-
en: 'English',
18-
es: 'Español',
19-
} as const;
20-
2112
export function LanguageSelector() {
22-
const { language, setLanguage } = useLanguage();
13+
const { language, setLanguage, availableLanguages } = useLanguage();
2314

2415
const handleLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
25-
const newLang = e.target.value as keyof typeof languages;
16+
const newLang = e.target.value as keyof typeof availableLanguages;
2617
setLanguage(newLang);
2718
};
2819

@@ -50,7 +41,7 @@ export function LanguageSelector() {
5041
className="bg-white/15 text-white text-sm pl-10 pr-8 py-2 rounded-lg appearance-none cursor-pointer hover:bg-white/20 transition-colors focus:outline-none focus:ring-2 focus:ring-white/30"
5142
aria-label="Select language"
5243
>
53-
{Object.entries(languages).map(([code, name]) => (
44+
{Object.entries(availableLanguages).map(([code, name]) => (
5445
<option key={code} value={code} className="bg-[#111827] text-white">
5546
{name}
5647
</option>

src/i18n/LanguageContext.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,28 @@
1111
import { useStore } from '@nanostores/react';
1212
import { atom } from 'nanostores';
1313

14-
import en from './en.json';
15-
import es from './es.json';
1614
import { LANGUAGE_NAMES } from './types';
1715

1816
import type { Translations, LanguageCode } from './types';
1917

18+
// Dynamically import all language files
19+
const languageModules = import.meta.glob<{ default: Translations }>('./*.json', { eager: true });
20+
2021
/**
2122
* Map of language codes to their translation data
2223
* @const languages
2324
*/
24-
const languages: Record<LanguageCode, Translations> = {
25-
en,
26-
es,
27-
} as const;
25+
const languages: Record<LanguageCode, Translations> = Object.entries(languageModules).reduce(
26+
(acc, [path, module]) => {
27+
// Extract language code from file path (e.g., './en.json' -> 'en')
28+
const langCode = path.match(/\.\/(\w+)\.json/)?.[1] as LanguageCode;
29+
if (langCode && langCode in LANGUAGE_NAMES) {
30+
acc[langCode] = module.default;
31+
}
32+
return acc;
33+
},
34+
{} as Record<LanguageCode, Translations>
35+
);
2836

2937
/**
3038
* Default language to use when no preference is stored
@@ -91,7 +99,7 @@ function getInitialLanguage(): LanguageCode {
9199

92100
// Create stores for reactive state management
93101
export const languageStore = atom<LanguageCode>(DEFAULT_LANGUAGE);
94-
export const translationsStore = atom<Translations>(en);
102+
export const translationsStore = atom<Translations>(languages[DEFAULT_LANGUAGE]);
95103

96104
// Initialize language and translations
97105
if (typeof window !== 'undefined') {

0 commit comments

Comments
 (0)