afro-locale 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +390 -0
- package/CHANGELOG.md +73 -0
- package/CODE_OF_CONDUCT.md +96 -0
- package/CONTRIBUTING.md +351 -0
- package/EXAMPLES.md +327 -0
- package/LICENSE +21 -0
- package/README.md +431 -0
- package/SECURITY.md +84 -0
- package/SUPPORT.md +264 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +167 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# Contributing to afro-locale
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to `@koadit/afro-locale`! We appreciate your help in making this library better for the African developer community.
|
|
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
|
+
- [Testing](#testing)
|
|
12
|
+
- [Submitting Changes](#submitting-changes)
|
|
13
|
+
- [Style Guide](#style-guide)
|
|
14
|
+
- [Commit Message Guidelines](#commit-message-guidelines)
|
|
15
|
+
|
|
16
|
+
## Code of Conduct
|
|
17
|
+
|
|
18
|
+
We are committed to providing a welcoming and inclusive environment for all contributors. Please be respectful and constructive in all interactions.
|
|
19
|
+
|
|
20
|
+
## Getting Started
|
|
21
|
+
|
|
22
|
+
1. Fork the repository on GitHub
|
|
23
|
+
2. Clone your fork locally
|
|
24
|
+
3. Create a new branch for your feature or fix
|
|
25
|
+
4. Make your changes
|
|
26
|
+
5. Push your branch to your fork
|
|
27
|
+
6. Submit a pull request
|
|
28
|
+
|
|
29
|
+
## Development Setup
|
|
30
|
+
|
|
31
|
+
### Prerequisites
|
|
32
|
+
|
|
33
|
+
- Node.js 14.0 or higher
|
|
34
|
+
- npm 6.0 or higher (or yarn/pnpm)
|
|
35
|
+
|
|
36
|
+
### Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Clone the repository
|
|
40
|
+
git clone https://github.com/oluokunkabiru/afro-locale.git
|
|
41
|
+
cd afro-locale
|
|
42
|
+
|
|
43
|
+
# Install dependencies
|
|
44
|
+
npm install
|
|
45
|
+
|
|
46
|
+
# Build the project
|
|
47
|
+
npm run build
|
|
48
|
+
|
|
49
|
+
# Run tests
|
|
50
|
+
npm test
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Available Commands
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Build for both CJS and ESM
|
|
57
|
+
npm run build
|
|
58
|
+
|
|
59
|
+
# Build CommonJS only
|
|
60
|
+
npm run build:cjs
|
|
61
|
+
|
|
62
|
+
# Build ESM only
|
|
63
|
+
npm run build:esm
|
|
64
|
+
|
|
65
|
+
# Run tests
|
|
66
|
+
npm test
|
|
67
|
+
|
|
68
|
+
# Run tests with coverage
|
|
69
|
+
npm run test:coverage
|
|
70
|
+
|
|
71
|
+
# Run linting
|
|
72
|
+
npm run lint
|
|
73
|
+
|
|
74
|
+
# Type check
|
|
75
|
+
npm run type-check
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Making Changes
|
|
79
|
+
|
|
80
|
+
### Branch Naming Convention
|
|
81
|
+
|
|
82
|
+
Use descriptive branch names:
|
|
83
|
+
- `feature/add-new-language` - for new features
|
|
84
|
+
- `fix/currency-formatting` - for bug fixes
|
|
85
|
+
- `docs/update-readme` - for documentation updates
|
|
86
|
+
- `test/improve-coverage` - for test improvements
|
|
87
|
+
|
|
88
|
+
### File Structure
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
afro-locale/
|
|
92
|
+
├── src/
|
|
93
|
+
│ ├── index.ts # Main library code
|
|
94
|
+
│ └── index.test.ts # Test suite
|
|
95
|
+
├── dist/ # Built files (auto-generated)
|
|
96
|
+
├── jest.config.js # Jest configuration
|
|
97
|
+
├── tsconfig.json # TypeScript configuration
|
|
98
|
+
├── .eslintrc.json # ESLint configuration
|
|
99
|
+
├── package.json # Package metadata
|
|
100
|
+
├── README.md # Documentation
|
|
101
|
+
├── CONTRIBUTING.md # This file
|
|
102
|
+
└── CHANGELOG.md # Version history
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Testing
|
|
106
|
+
|
|
107
|
+
### Writing Tests
|
|
108
|
+
|
|
109
|
+
Tests are located in `src/index.test.ts`. When adding new features or fixing bugs, please include corresponding tests.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
describe('Feature Name', () => {
|
|
113
|
+
it('should behave correctly', () => {
|
|
114
|
+
// Arrange
|
|
115
|
+
const input = ...;
|
|
116
|
+
|
|
117
|
+
// Act
|
|
118
|
+
const result = ...;
|
|
119
|
+
|
|
120
|
+
// Assert
|
|
121
|
+
expect(result).toBe(...);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Running Tests
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Run all tests
|
|
130
|
+
npm test
|
|
131
|
+
|
|
132
|
+
# Run tests in watch mode
|
|
133
|
+
npm test -- --watch
|
|
134
|
+
|
|
135
|
+
# Run tests with coverage
|
|
136
|
+
npm run test:coverage
|
|
137
|
+
|
|
138
|
+
# Run specific test file
|
|
139
|
+
npm test -- src/index.test.ts
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Coverage Requirements
|
|
143
|
+
|
|
144
|
+
- Aim for at least 80% code coverage
|
|
145
|
+
- All new features should have tests
|
|
146
|
+
- Bug fixes should include regression tests
|
|
147
|
+
|
|
148
|
+
## Submitting Changes
|
|
149
|
+
|
|
150
|
+
### Before You Submit
|
|
151
|
+
|
|
152
|
+
1. Ensure all tests pass: `npm test`
|
|
153
|
+
2. Run linting: `npm run lint`
|
|
154
|
+
3. Run type checking: `npm run type-check`
|
|
155
|
+
4. Update the README if necessary
|
|
156
|
+
5. Add an entry to CHANGELOG.md
|
|
157
|
+
|
|
158
|
+
### Creating a Pull Request
|
|
159
|
+
|
|
160
|
+
1. Push your branch to your fork
|
|
161
|
+
2. Navigate to the original repository
|
|
162
|
+
3. Click "New Pull Request"
|
|
163
|
+
4. Fill in the PR template with:
|
|
164
|
+
- Clear title describing the change
|
|
165
|
+
- Description of what changed and why
|
|
166
|
+
- Related issue numbers (if applicable)
|
|
167
|
+
- Checklist of changes
|
|
168
|
+
|
|
169
|
+
### PR Template
|
|
170
|
+
|
|
171
|
+
```markdown
|
|
172
|
+
## Description
|
|
173
|
+
Brief description of the changes
|
|
174
|
+
|
|
175
|
+
## Related Issues
|
|
176
|
+
Closes #123
|
|
177
|
+
|
|
178
|
+
## Type of Change
|
|
179
|
+
- [ ] Bug fix
|
|
180
|
+
- [ ] New feature
|
|
181
|
+
- [ ] Documentation update
|
|
182
|
+
- [ ] Performance improvement
|
|
183
|
+
|
|
184
|
+
## Testing
|
|
185
|
+
- [ ] Added tests for new functionality
|
|
186
|
+
- [ ] All tests pass
|
|
187
|
+
- [ ] Code covered by tests
|
|
188
|
+
|
|
189
|
+
## Checklist
|
|
190
|
+
- [ ] My code follows the style guidelines
|
|
191
|
+
- [ ] I have updated documentation
|
|
192
|
+
- [ ] I have added tests for my changes
|
|
193
|
+
- [ ] My changes generate no new warnings
|
|
194
|
+
- [ ] I have updated the CHANGELOG.md
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Style Guide
|
|
198
|
+
|
|
199
|
+
### TypeScript
|
|
200
|
+
|
|
201
|
+
- Use TypeScript strict mode
|
|
202
|
+
- Add JSDoc comments for public APIs
|
|
203
|
+
- Use meaningful variable and function names
|
|
204
|
+
- Keep functions small and focused
|
|
205
|
+
- Prefer interfaces over type aliases for public APIs
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
/**
|
|
209
|
+
* Format a number according to locale conventions
|
|
210
|
+
* @param value - The number to format
|
|
211
|
+
* @param options - Optional formatting options
|
|
212
|
+
* @returns Formatted number string
|
|
213
|
+
*/
|
|
214
|
+
public formatNumber(value: number, options?: Intl.NumberFormatOptions): string {
|
|
215
|
+
// Implementation
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Code Quality
|
|
220
|
+
|
|
221
|
+
- Use `const` by default, `let` if reassignment is needed
|
|
222
|
+
- Avoid `var`
|
|
223
|
+
- Avoid `any` types - prefer specific types or generics
|
|
224
|
+
- Keep lines under 100 characters when possible
|
|
225
|
+
- Use meaningful comments for complex logic
|
|
226
|
+
|
|
227
|
+
### Imports
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// Group imports logically
|
|
231
|
+
import type { TypeA, TypeB } from './types';
|
|
232
|
+
import { functionA, functionB } from './utils';
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Commit Message Guidelines
|
|
236
|
+
|
|
237
|
+
Follow the Conventional Commits specification:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
<type>(<scope>): <subject>
|
|
241
|
+
|
|
242
|
+
<body>
|
|
243
|
+
|
|
244
|
+
<footer>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Type
|
|
248
|
+
|
|
249
|
+
- `feat`: A new feature
|
|
250
|
+
- `fix`: A bug fix
|
|
251
|
+
- `docs`: Documentation only changes
|
|
252
|
+
- `style`: Changes that don't affect code meaning (formatting, semicolons, etc.)
|
|
253
|
+
- `refactor`: Code change that neither fixes a bug nor adds a feature
|
|
254
|
+
- `perf`: Code change that improves performance
|
|
255
|
+
- `test`: Adding missing tests or correcting existing tests
|
|
256
|
+
- `chore`: Changes to build process, dependencies, etc.
|
|
257
|
+
|
|
258
|
+
### Scope
|
|
259
|
+
|
|
260
|
+
Optional - the section of the codebase affected (e.g., `currency`, `locale`, `formatting`)
|
|
261
|
+
|
|
262
|
+
### Subject
|
|
263
|
+
|
|
264
|
+
- Use imperative, present tense: "add" not "added" or "adds"
|
|
265
|
+
- Don't capitalize first letter
|
|
266
|
+
- No period (.) at the end
|
|
267
|
+
- Limit to 50 characters
|
|
268
|
+
|
|
269
|
+
### Body
|
|
270
|
+
|
|
271
|
+
- Explain what and why, not how
|
|
272
|
+
- Wrap at 72 characters
|
|
273
|
+
- Separate from subject with a blank line
|
|
274
|
+
|
|
275
|
+
### Footer
|
|
276
|
+
|
|
277
|
+
- Reference issues: `Closes #123`, `Fixes #456`
|
|
278
|
+
- Note breaking changes: `BREAKING CHANGE: description`
|
|
279
|
+
|
|
280
|
+
### Examples
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
feat(currency): add support for XOF currency
|
|
284
|
+
|
|
285
|
+
Add West African CFA franc (XOF) to supported currencies.
|
|
286
|
+
Includes proper formatting with correct decimal places.
|
|
287
|
+
|
|
288
|
+
Closes #42
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
fix(locale): fix locale normalization for en-US
|
|
293
|
+
|
|
294
|
+
Previously, setting locale to en-US was not properly
|
|
295
|
+
normalized to en. This is now fixed.
|
|
296
|
+
|
|
297
|
+
Fixes #38
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Adding Support for New Languages
|
|
301
|
+
|
|
302
|
+
To add support for a new language:
|
|
303
|
+
|
|
304
|
+
1. Update the `languages` map in `src/index.ts`
|
|
305
|
+
2. Add test cases in `src/index.test.ts`
|
|
306
|
+
3. Update the README.md with the new language
|
|
307
|
+
4. Update CHANGELOG.md
|
|
308
|
+
5. Submit a PR with these changes
|
|
309
|
+
|
|
310
|
+
Example:
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
private languages: Record<string, LocaleConfig> = {
|
|
314
|
+
// ... existing languages
|
|
315
|
+
xx: { code: 'xx', name: 'Language Name', nativeName: 'Native Name', country: 'XX' },
|
|
316
|
+
};
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Adding Support for New Currencies
|
|
320
|
+
|
|
321
|
+
To add support for a new currency:
|
|
322
|
+
|
|
323
|
+
1. Update the `currencyFormats` map in `src/index.ts`
|
|
324
|
+
2. Add test cases in `src/index.test.ts`
|
|
325
|
+
3. Update the README.md with the new currency
|
|
326
|
+
4. Update CHANGELOG.md
|
|
327
|
+
5. Submit a PR with these changes
|
|
328
|
+
|
|
329
|
+
Example:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
private currencyFormats: Record<string, CurrencyFormat> = {
|
|
333
|
+
// ... existing currencies
|
|
334
|
+
XXX: { symbol: 'ₓ', position: 'left', decimals: 2 },
|
|
335
|
+
};
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Questions?
|
|
339
|
+
|
|
340
|
+
- Open an issue on GitHub for bugs and features
|
|
341
|
+
- Start a discussion for questions and ideas
|
|
342
|
+
- Email: support@koadit.com
|
|
343
|
+
|
|
344
|
+
## Recognition
|
|
345
|
+
|
|
346
|
+
Contributors will be recognized in:
|
|
347
|
+
- CHANGELOG.md
|
|
348
|
+
- GitHub contributors page
|
|
349
|
+
- Project documentation
|
|
350
|
+
|
|
351
|
+
Thank you for contributing to make afro-locale better! 🚀
|
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
This directory contains practical examples of using `@koadit/afro-locale`.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
### CommonJS Example
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
const { locale } = require('@koadit/afro-locale');
|
|
11
|
+
|
|
12
|
+
// Set locale
|
|
13
|
+
locale.set('yo'); // Yoruba
|
|
14
|
+
|
|
15
|
+
// Format currency
|
|
16
|
+
console.log(locale.formatCurrency(5000, 'NGN'));
|
|
17
|
+
// Output: ₦5,000.00
|
|
18
|
+
|
|
19
|
+
// Format date
|
|
20
|
+
console.log(locale.formatDate(new Date(), 'long'));
|
|
21
|
+
// Output: 9 Ȁpẹ̀lẹ̀ 2026
|
|
22
|
+
|
|
23
|
+
// Get language info
|
|
24
|
+
console.log(locale.getLanguageName('yo')); // "Yoruba"
|
|
25
|
+
console.log(locale.getLanguageNativeName('yo')); // "Yorùbá"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### ES Module Example
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import { locale } from '@koadit/afro-locale';
|
|
32
|
+
|
|
33
|
+
locale.set('sw');
|
|
34
|
+
console.log(locale.formatCurrency(1000, 'KES'));
|
|
35
|
+
// Output: KSh1,000.00
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## E-Commerce Example
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import { locale } from '@koadit/afro-locale';
|
|
42
|
+
|
|
43
|
+
class ProductFormatter {
|
|
44
|
+
constructor(userLanguage, userCountry) {
|
|
45
|
+
this.userLanguage = userLanguage;
|
|
46
|
+
this.userCountry = userCountry;
|
|
47
|
+
locale.set(userLanguage);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
formatPrice(price, currency) {
|
|
51
|
+
return locale.formatCurrency(price, currency);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
formatAvailableDate(date) {
|
|
55
|
+
return locale.formatDate(date, 'long');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getLanguageDisplay() {
|
|
59
|
+
const name = locale.getLanguageName(this.userLanguage);
|
|
60
|
+
const native = locale.getLanguageNativeName(this.userLanguage);
|
|
61
|
+
return `${name} (${native})`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Usage
|
|
66
|
+
const formatter = new ProductFormatter('yo', 'NG');
|
|
67
|
+
console.log(formatter.formatPrice(5000, 'NGN')); // ₦5,000.00
|
|
68
|
+
console.log(formatter.getLanguageDisplay()); // Yoruba (Yorùbá)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Multi-Language Support
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
import { locale } from '@koadit/afro-locale';
|
|
75
|
+
|
|
76
|
+
const priceInUSD = 50;
|
|
77
|
+
const countries = {
|
|
78
|
+
'Nigeria': { lang: 'yo', currency: 'NGN', rate: 412 },
|
|
79
|
+
'Kenya': { lang: 'sw', currency: 'KES', rate: 139 },
|
|
80
|
+
'South Africa': { lang: 'zu', currency: 'ZAR', rate: 18.5 },
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
console.log('Prices in different markets:');
|
|
84
|
+
for (const [country, { lang, currency, rate }] of Object.entries(countries)) {
|
|
85
|
+
locale.set(lang);
|
|
86
|
+
const localPrice = priceInUSD * rate;
|
|
87
|
+
const formatted = locale.formatCurrency(localPrice, currency);
|
|
88
|
+
const langName = locale.getLanguageNativeName(lang);
|
|
89
|
+
console.log(`${country} (${langName}): ${formatted}`);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## React Example
|
|
94
|
+
|
|
95
|
+
```jsx
|
|
96
|
+
import React, { useState, useMemo } from 'react';
|
|
97
|
+
import { locale } from '@koadit/afro-locale';
|
|
98
|
+
|
|
99
|
+
function LanguageSelector() {
|
|
100
|
+
const [currentLang, setCurrentLang] = useState('en');
|
|
101
|
+
const languages = useMemo(() => locale.getSupportedLanguages(), []);
|
|
102
|
+
|
|
103
|
+
const handleLanguageChange = (event) => {
|
|
104
|
+
const newLang = event.target.value;
|
|
105
|
+
locale.set(newLang);
|
|
106
|
+
setCurrentLang(newLang);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<select value={currentLang} onChange={handleLanguageChange}>
|
|
111
|
+
{languages.map(lang => (
|
|
112
|
+
<option key={lang.code} value={lang.code}>
|
|
113
|
+
{lang.nativeName}
|
|
114
|
+
</option>
|
|
115
|
+
))}
|
|
116
|
+
</select>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function PriceDisplay({ amount, currency }) {
|
|
121
|
+
const formatted = useMemo(
|
|
122
|
+
() => locale.formatCurrency(amount, currency),
|
|
123
|
+
[amount, currency]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return <span>{formatted}</span>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default function App() {
|
|
130
|
+
return (
|
|
131
|
+
<div>
|
|
132
|
+
<LanguageSelector />
|
|
133
|
+
<PriceDisplay amount={5000} currency="NGN" />
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Vue Example
|
|
140
|
+
|
|
141
|
+
```vue
|
|
142
|
+
<template>
|
|
143
|
+
<div>
|
|
144
|
+
<select v-model="currentLang" @change="setLanguage">
|
|
145
|
+
<option v-for="lang in languages" :key="lang.code" :value="lang.code">
|
|
146
|
+
{{ lang.nativeName }}
|
|
147
|
+
</option>
|
|
148
|
+
</select>
|
|
149
|
+
<p>Price: {{ formatPrice(5000, 'NGN') }}</p>
|
|
150
|
+
</div>
|
|
151
|
+
</template>
|
|
152
|
+
|
|
153
|
+
<script>
|
|
154
|
+
import { ref, computed } from 'vue';
|
|
155
|
+
import { locale } from '@koadit/afro-locale';
|
|
156
|
+
|
|
157
|
+
export default {
|
|
158
|
+
name: 'LanguageExample',
|
|
159
|
+
setup() {
|
|
160
|
+
const currentLang = ref('en');
|
|
161
|
+
const languages = computed(() => locale.getSupportedLanguages());
|
|
162
|
+
|
|
163
|
+
const setLanguage = (lang) => {
|
|
164
|
+
locale.set(lang);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const formatPrice = (amount, currency) => {
|
|
168
|
+
return locale.formatCurrency(amount, currency);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
currentLang,
|
|
173
|
+
languages,
|
|
174
|
+
setLanguage,
|
|
175
|
+
formatPrice,
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
</script>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## TypeScript Example
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import AfroLocale, { locale, LocaleConfig } from '@koadit/afro-locale';
|
|
186
|
+
|
|
187
|
+
class LocalizationService {
|
|
188
|
+
private locale: AfroLocale;
|
|
189
|
+
|
|
190
|
+
constructor() {
|
|
191
|
+
this.locale = new AfroLocale();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
setLanguage(code: string): boolean {
|
|
195
|
+
if (this.locale.isLocaleSupported(code)) {
|
|
196
|
+
this.locale.set(code);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
formatPrice(amount: number, currency: string): string {
|
|
203
|
+
return this.locale.formatCurrency(amount, currency);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
formatEventDate(date: Date): string {
|
|
207
|
+
return this.locale.formatDateTime(date, 'long');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getAvailableLanguages(): LocaleConfig[] {
|
|
211
|
+
return this.locale.getSupportedLanguages();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const service = new LocalizationService();
|
|
216
|
+
service.setLanguage('sw');
|
|
217
|
+
console.log(service.formatPrice(1000, 'KES')); // KSh1,000.00
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Form Localization Example
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
<!DOCTYPE html>
|
|
224
|
+
<html>
|
|
225
|
+
<head>
|
|
226
|
+
<title>Form Localization Example</title>
|
|
227
|
+
</head>
|
|
228
|
+
<body>
|
|
229
|
+
<form id="pricing-form">
|
|
230
|
+
<label>
|
|
231
|
+
Language:
|
|
232
|
+
<select id="language">
|
|
233
|
+
<option value="en">English</option>
|
|
234
|
+
<option value="yo">Yoruba</option>
|
|
235
|
+
<option value="sw">Swahili</option>
|
|
236
|
+
</select>
|
|
237
|
+
</label>
|
|
238
|
+
|
|
239
|
+
<label>
|
|
240
|
+
Amount:
|
|
241
|
+
<input type="number" id="amount" value="5000" />
|
|
242
|
+
</label>
|
|
243
|
+
|
|
244
|
+
<label>
|
|
245
|
+
Currency:
|
|
246
|
+
<select id="currency">
|
|
247
|
+
<option value="NGN">NGN</option>
|
|
248
|
+
<option value="KES">KES</option>
|
|
249
|
+
<option value="ZAR">ZAR</option>
|
|
250
|
+
</select>
|
|
251
|
+
</label>
|
|
252
|
+
|
|
253
|
+
<output id="result"></output>
|
|
254
|
+
</form>
|
|
255
|
+
|
|
256
|
+
<script type="module">
|
|
257
|
+
import { locale } from '@koadit/afro-locale';
|
|
258
|
+
|
|
259
|
+
const form = document.getElementById('pricing-form');
|
|
260
|
+
const languageSelect = document.getElementById('language');
|
|
261
|
+
const amountInput = document.getElementById('amount');
|
|
262
|
+
const currencySelect = document.getElementById('currency');
|
|
263
|
+
const result = document.getElementById('result');
|
|
264
|
+
|
|
265
|
+
function updateResult() {
|
|
266
|
+
const lang = languageSelect.value;
|
|
267
|
+
const amount = parseInt(amountInput.value) || 0;
|
|
268
|
+
const currency = currencySelect.value;
|
|
269
|
+
|
|
270
|
+
locale.set(lang);
|
|
271
|
+
const formatted = locale.formatCurrency(amount, currency);
|
|
272
|
+
result.textContent = formatted;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
form.addEventListener('change', updateResult);
|
|
276
|
+
form.addEventListener('input', updateResult);
|
|
277
|
+
|
|
278
|
+
updateResult();
|
|
279
|
+
</script>
|
|
280
|
+
</body>
|
|
281
|
+
</html>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## API Integration Example
|
|
285
|
+
|
|
286
|
+
```javascript
|
|
287
|
+
import { locale } from '@koadit/afro-locale';
|
|
288
|
+
|
|
289
|
+
class APIClient {
|
|
290
|
+
constructor(userLanguage = 'en') {
|
|
291
|
+
locale.set(userLanguage);
|
|
292
|
+
this.userLanguage = userLanguage;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async getProductPrice(productId) {
|
|
296
|
+
const response = await fetch(`/api/products/${productId}`);
|
|
297
|
+
const data = await response.json();
|
|
298
|
+
|
|
299
|
+
// Format price based on user locale
|
|
300
|
+
const currency = this.getCurrencyForLocale();
|
|
301
|
+
const formatted = locale.formatCurrency(data.price, currency);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
...data,
|
|
305
|
+
formattedPrice: formatted,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
getCurrencyForLocale() {
|
|
310
|
+
const currencyMap = {
|
|
311
|
+
'yo': 'NGN', // Yoruba
|
|
312
|
+
'sw': 'KES', // Swahili
|
|
313
|
+
'zu': 'ZAR', // Zulu
|
|
314
|
+
};
|
|
315
|
+
return currencyMap[this.userLanguage] || 'USD';
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Usage
|
|
320
|
+
const client = new APIClient('yo');
|
|
321
|
+
const product = await client.getProductPrice(123);
|
|
322
|
+
console.log(product.formattedPrice);
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
For more information, see the [README.md](../README.md) and [API.md](../API.md).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KOADIT Digital Solutions
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|