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 ADDED
@@ -0,0 +1,390 @@
1
+ # API Reference
2
+
3
+ Complete API reference for `@koadit/afro-locale`.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Classes](#classes)
8
+ - [Interfaces](#interfaces)
9
+ - [Enums & Constants](#enums--constants)
10
+ - [Singleton Instance](#singleton-instance)
11
+ - [Usage Patterns](#usage-patterns)
12
+
13
+ ## Classes
14
+
15
+ ### AfroLocale
16
+
17
+ Main class for handling localization.
18
+
19
+ ```typescript
20
+ export class AfroLocale {
21
+ constructor();
22
+
23
+ // Locale Management
24
+ set(locale: string): void;
25
+ get(): string;
26
+ detectLocale(): string;
27
+ isLocaleSupported(locale: string): boolean;
28
+
29
+ // Formatting
30
+ formatCurrency(amount: number, currency: string): string;
31
+ formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
32
+ formatDate(date: Date, style?: 'short' | 'long'): string;
33
+ formatTime(date: Date, style?: 'short' | 'long'): string;
34
+ formatDateTime(date: Date, style?: 'short' | 'long'): string;
35
+
36
+ // Language Information
37
+ getLanguageName(code: string): string;
38
+ getLanguageNativeName(code: string): string;
39
+ getSupportedLanguages(): LocaleConfig[];
40
+ getSupportedCurrencies(): string[];
41
+ }
42
+ ```
43
+
44
+ ## Interfaces
45
+
46
+ ### LocaleConfig
47
+
48
+ Configuration object for a supported locale.
49
+
50
+ ```typescript
51
+ export interface LocaleConfig {
52
+ code: string; // Language code (e.g., 'yo', 'sw')
53
+ name: string; // English language name
54
+ nativeName: string; // Language name in native script
55
+ country?: string; // ISO 3166-1 alpha-2 country code
56
+ }
57
+ ```
58
+
59
+ **Example:**
60
+ ```typescript
61
+ {
62
+ code: 'yo',
63
+ name: 'Yoruba',
64
+ nativeName: 'Yorùbá',
65
+ country: 'NG'
66
+ }
67
+ ```
68
+
69
+ ### CurrencyFormat
70
+
71
+ Configuration for currency formatting.
72
+
73
+ ```typescript
74
+ export interface CurrencyFormat {
75
+ symbol: string; // Currency symbol
76
+ position: 'left' | 'right'; // Symbol position relative to amount
77
+ decimals: number; // Number of decimal places
78
+ }
79
+ ```
80
+
81
+ **Example:**
82
+ ```typescript
83
+ {
84
+ symbol: '₦',
85
+ position: 'left',
86
+ decimals: 2
87
+ }
88
+ ```
89
+
90
+ ## Enums & Constants
91
+
92
+ ### Supported Languages
93
+
94
+ ```typescript
95
+ 'am' | 'bn' | 'en' | 'ff' | 'ha' | 'ig' | 'ki' | 'ln' | 'lu' |
96
+ 'mg' | 'ny' | 'om' | 'rw' | 'sn' | 'so' | 'st' | 'sw' | 'ti' |
97
+ 'tn' | 'xh' | 'yo' | 'zu'
98
+ ```
99
+
100
+ ### Supported Currencies
101
+
102
+ ```typescript
103
+ 'ETB' | 'EUR' | 'GHS' | 'KES' | 'MWK' | 'NGN' | 'RWF' |
104
+ 'TZS' | 'UGX' | 'USD' | 'XAF' | 'XOF' | 'ZAR' | 'ZMW'
105
+ ```
106
+
107
+ ### Format Styles
108
+
109
+ ```typescript
110
+ type FormatStyle = 'short' | 'long';
111
+ ```
112
+
113
+ ## Singleton Instance
114
+
115
+ ### locale
116
+
117
+ Pre-created singleton instance ready to use.
118
+
119
+ ```typescript
120
+ export const locale: AfroLocale;
121
+
122
+ // Usage
123
+ import { locale } from '@koadit/afro-locale';
124
+ locale.set('yo');
125
+ locale.formatCurrency(5000, 'NGN');
126
+ ```
127
+
128
+ ### Creating Custom Instances
129
+
130
+ ```typescript
131
+ import AfroLocale from '@koadit/afro-locale';
132
+
133
+ const locale1 = new AfroLocale();
134
+ const locale2 = new AfroLocale();
135
+
136
+ locale1.set('yo');
137
+ locale2.set('sw');
138
+
139
+ // Each instance maintains its own state
140
+ ```
141
+
142
+ ## Usage Patterns
143
+
144
+ ### Pattern 1: Singleton Pattern
145
+
146
+ Best for most applications.
147
+
148
+ ```typescript
149
+ import { locale } from '@koadit/afro-locale';
150
+
151
+ locale.set('yo');
152
+ const formatted = locale.formatCurrency(5000, 'NGN');
153
+ ```
154
+
155
+ ### Pattern 2: Instance Pattern
156
+
157
+ Use when you need to manage multiple independent locales.
158
+
159
+ ```typescript
160
+ import AfroLocale from '@koadit/afro-locale';
161
+
162
+ const yoruba = new AfroLocale();
163
+ const swahili = new AfroLocale();
164
+
165
+ yoruba.set('yo');
166
+ swahili.set('sw');
167
+
168
+ console.log(yoruba.formatCurrency(5000, 'NGN'));
169
+ console.log(swahili.formatCurrency(5000, 'KES'));
170
+ ```
171
+
172
+ ### Pattern 3: Factory Pattern
173
+
174
+ Create a factory for managing locale instances.
175
+
176
+ ```typescript
177
+ import AfroLocale from '@koadit/afro-locale';
178
+
179
+ class LocaleFactory {
180
+ private instances: Map<string, AfroLocale> = new Map();
181
+
182
+ getLocale(lang: string): AfroLocale {
183
+ if (!this.instances.has(lang)) {
184
+ const instance = new AfroLocale();
185
+ instance.set(lang);
186
+ this.instances.set(lang, instance);
187
+ }
188
+ return this.instances.get(lang)!;
189
+ }
190
+ }
191
+
192
+ const factory = new LocaleFactory();
193
+ const yoruba = factory.getLocale('yo');
194
+ ```
195
+
196
+ ### Pattern 4: Functional Wrapper
197
+
198
+ Create helper functions for common operations.
199
+
200
+ ```typescript
201
+ import { locale } from '@koadit/afro-locale';
202
+
203
+ function formatPrice(price: number, lang: string, currency: string): string {
204
+ locale.set(lang);
205
+ return locale.formatCurrency(price, currency);
206
+ }
207
+
208
+ // Usage
209
+ formatPrice(5000, 'yo', 'NGN'); // ₦5,000.00
210
+ ```
211
+
212
+ ### Pattern 5: Locale Provider (React)
213
+
214
+ Managing locale in React applications.
215
+
216
+ ```typescript
217
+ import React, { createContext, useContext, useState } from 'react';
218
+ import { locale } from '@koadit/afro-locale';
219
+
220
+ const LocaleContext = createContext<string>('en');
221
+
222
+ export function LocaleProvider({ children }) {
223
+ const [currentLocale, setCurrentLocale] = useState('en');
224
+
225
+ const handleSetLocale = (newLocale: string) => {
226
+ locale.set(newLocale);
227
+ setCurrentLocale(newLocale);
228
+ };
229
+
230
+ return (
231
+ <LocaleContext.Provider value={currentLocale}>
232
+ {children}
233
+ </LocaleContext.Provider>
234
+ );
235
+ }
236
+
237
+ export function useLocale() {
238
+ return useContext(LocaleContext);
239
+ }
240
+ ```
241
+
242
+ ### Pattern 6: Vue Composition API
243
+
244
+ Managing locale in Vue applications.
245
+
246
+ ```typescript
247
+ import { ref, provide, inject } from 'vue';
248
+ import { locale } from '@koadit/afro-locale';
249
+
250
+ export function useLocale() {
251
+ const currentLocale = ref('en');
252
+
253
+ const setLocale = (newLocale: string) => {
254
+ locale.set(newLocale);
255
+ currentLocale.value = newLocale;
256
+ };
257
+
258
+ provide('locale', { currentLocale, setLocale });
259
+ }
260
+
261
+ export function useCurrentLocale() {
262
+ return inject('locale');
263
+ }
264
+ ```
265
+
266
+ ## Error Handling
267
+
268
+ ### Handling Invalid Locale
269
+
270
+ ```typescript
271
+ const locale = new AfroLocale();
272
+
273
+ if (locale.isLocaleSupported('xx')) {
274
+ locale.set('xx');
275
+ } else {
276
+ console.warn('Locale not supported, using English');
277
+ locale.set('en');
278
+ }
279
+ ```
280
+
281
+ ### Handling Invalid Currency
282
+
283
+ ```typescript
284
+ try {
285
+ const formatted = locale.formatCurrency(5000, 'INVALID');
286
+ console.log(formatted); // "INVALID 5000.00"
287
+ } catch (error) {
288
+ console.error('Currency error:', error);
289
+ }
290
+ ```
291
+
292
+ ## Type Safety
293
+
294
+ ### TypeScript
295
+
296
+ ```typescript
297
+ import AfroLocale, { locale, LocaleConfig } from '@koadit/afro-locale';
298
+
299
+ // Type-safe locale setting
300
+ const supportedLocales: LocaleConfig[] = locale.getSupportedLanguages();
301
+
302
+ for (const config of supportedLocales) {
303
+ locale.set(config.code);
304
+ console.log(`${config.name}: ${config.nativeName}`);
305
+ }
306
+ ```
307
+
308
+ ## Performance Considerations
309
+
310
+ ### Caching Formatted Values
311
+
312
+ ```typescript
313
+ const cache = new Map<string, string>();
314
+
315
+ function getCachedFormatted(
316
+ amount: number,
317
+ currency: string,
318
+ lang: string
319
+ ): string {
320
+ const key = `${amount}-${currency}-${lang}`;
321
+
322
+ if (cache.has(key)) {
323
+ return cache.get(key)!;
324
+ }
325
+
326
+ locale.set(lang);
327
+ const formatted = locale.formatCurrency(amount, currency);
328
+ cache.set(key, formatted);
329
+
330
+ return formatted;
331
+ }
332
+ ```
333
+
334
+ ### Lazy Locale Detection
335
+
336
+ ```typescript
337
+ let detectedLocale: string | null = null;
338
+
339
+ function getDetectedLocale(): string {
340
+ if (detectedLocale === null) {
341
+ detectedLocale = locale.detectLocale();
342
+ }
343
+ return detectedLocale;
344
+ }
345
+ ```
346
+
347
+ ## Advanced Usage
348
+
349
+ ### Internationalize Custom Objects
350
+
351
+ ```typescript
352
+ interface Product {
353
+ name: string;
354
+ price: number;
355
+ currency: string;
356
+ }
357
+
358
+ function formatProduct(product: Product, lang: string): string {
359
+ locale.set(lang);
360
+ const formatted = locale.formatCurrency(product.price, product.currency);
361
+ return `${product.name}: ${formatted}`;
362
+ }
363
+ ```
364
+
365
+ ### Create Custom Formatter
366
+
367
+ ```typescript
368
+ class CustomFormatter {
369
+ private locale: AfroLocale;
370
+
371
+ constructor(locale: AfroLocale) {
372
+ this.locale = locale;
373
+ }
374
+
375
+ formatPrice(amount: number, currency: string): string {
376
+ return `Price: ${this.locale.formatCurrency(amount, currency)}`;
377
+ }
378
+
379
+ formatEvent(date: Date): string {
380
+ return `Date: ${this.locale.formatDateTime(date)}`;
381
+ }
382
+ }
383
+
384
+ const formatter = new CustomFormatter(locale);
385
+ console.log(formatter.formatPrice(5000, 'NGN'));
386
+ ```
387
+
388
+ ---
389
+
390
+ For more examples, see [README.md](README.md) and check the test suite in `src/index.test.ts`.
package/CHANGELOG.md ADDED
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-04-09
9
+
10
+ ### Added
11
+
12
+ - Initial release of `@koadit/afro-locale`
13
+ - Support for 21+ African languages with native names and country codes
14
+ - West African: Yoruba, Hausa, Igbo, Bambara, Fulfulde
15
+ - East African: Swahili, Kikuyu, Oromo, Somali, Tigrinya
16
+ - Southern African: Zulu, Xhosa, Sotho, Tswana, Shona
17
+ - Central African: Lingala, Luba-Katanga
18
+ - Other: Amharic, Kinyarwanda, Chichewa, Malagasy, English
19
+ - Support for 14+ African currencies (NGN, KES, ZAR, ETB, UGX, XOF, XAF, etc.)
20
+ - Currency formatting with proper symbols and decimal places
21
+ - Number formatting with locale-specific separators
22
+ - Date and time localization
23
+ - Datetime formatting
24
+ - Automatic locale detection from browser navigator
25
+ - Language information methods (name, native name)
26
+ - Locale validation and normalization
27
+ - Full TypeScript support with interfaces and types
28
+ - ESM and CommonJS module support
29
+ - Comprehensive test suite (28+ test cases)
30
+ - ESLint configuration for code quality
31
+ - Jest configuration for testing
32
+ - Full API documentation in README
33
+ - Contributing guidelines
34
+ - Changelog
35
+
36
+ ### Features
37
+
38
+ - **Multi-language Support**: 21+ African languages
39
+ - **Currency Formatting**: 14+ African currencies
40
+ - **Date/Time Localization**: Format dates, times, and datetimes
41
+ - **Locale Detection**: Automatic detection from browser
42
+ - **Zero Dependencies**: Built on native Web APIs
43
+ - **Type Safe**: Full TypeScript support
44
+ - **Well Tested**: 28+ unit tests with comprehensive coverage
45
+ - **ESM & CommonJS**: Works with both module systems
46
+
47
+ ### Technical Details
48
+
49
+ - Built with TypeScript 5.4
50
+ - Jest for unit testing
51
+ - ESLint for code quality
52
+ - Automatic builds for both CJS and ESM
53
+ - Type definitions included
54
+ - Source maps for debugging
55
+
56
+ ## Roadmap
57
+
58
+ ### Planned for Future Releases
59
+
60
+ - [ ] Additional African languages
61
+ - [ ] Plural form handling
62
+ - [ ] Message translation support (i18n framework)
63
+ - [ ] Relative time formatting (e.g., "2 days ago")
64
+ - [ ] More currency support
65
+ - [ ] React hooks for locale management
66
+ - [ ] Vue composables for locale management
67
+ - [ ] CLI tools for batch translations
68
+ - [ ] Web component for language selector
69
+ - [ ] Locale persistence (localStorage/sessionStorage)
70
+
71
+ ---
72
+
73
+ For migration information and breaking changes, please refer to the main README.
@@ -0,0 +1,96 @@
1
+ # Code of Conduct
2
+
3
+ ## Our Commitment
4
+
5
+ We are committed to providing a welcoming and inspiring community for all. We expect all members of the community to adhere to this code of conduct in all spaces managed by the afro-locale project, including but not limited to:
6
+
7
+ - GitHub repositories
8
+ - Issue trackers
9
+ - Pull request discussions
10
+ - Community forums and discussions
11
+ - Social media channels
12
+ - Conferences and meetups
13
+
14
+ ## Our Standards
15
+
16
+ Examples of behavior that contributes to creating a positive environment include:
17
+
18
+ - Using welcoming and inclusive language
19
+ - Being respectful of differing opinions, viewpoints, and experiences
20
+ - Gracefully accepting constructive criticism
21
+ - Focusing on what is best for the community
22
+ - Showing empathy towards other community members
23
+ - Encouraging and supporting new contributors
24
+
25
+ Examples of unacceptable behavior include:
26
+
27
+ - Harassment, bullying, or threatening language or gestures
28
+ - Discriminatory jokes and language
29
+ - Posting sexually explicit or violent material
30
+ - Posting (or threatening to post) others' personally identifying information ("doxing")
31
+ - Personal attacks or insults
32
+ - Unwelcome sexual attention
33
+ - Advocating for, or encouraging, any of the above behavior
34
+ - Sustained disruption of community spaces
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies to all community spaces and extends to:
39
+ - Public repositories
40
+ - Private repositories
41
+ - Issues and pull requests
42
+ - Comments and discussions
43
+ - External channels and mailing lists
44
+ - Organizational events and gatherings
45
+
46
+ ## Enforcement
47
+
48
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the community team at **conduct@koadit.com**. All complaints will be reviewed and investigated promptly and fairly.
49
+
50
+ ### Reporting
51
+
52
+ If you are subject to or witness unacceptable behavior, or have any other concerns, please notify us by emailing **conduct@koadit.com** with:
53
+
54
+ - Your contact information
55
+ - Names (real or usernames) of any individuals involved
56
+ - Description of the incident
57
+ - Context and additional details
58
+ - Any evidence or screenshots
59
+
60
+ All report contents will be kept confidential and only shared with people who need to know to address the issue.
61
+
62
+ ### Response
63
+
64
+ Community moderators are obligated to:
65
+ - Maintain confidentiality with respect to the reporter
66
+ - Review and investigate all complaints fairly
67
+ - Take appropriate action in response
68
+
69
+ ### Consequences
70
+
71
+ Community members who violate this Code of Conduct may face:
72
+ - Warning and removal from community spaces
73
+ - Temporary or permanent ban from community spaces
74
+ - Removal of contributed content
75
+ - Action as determined by community moderators
76
+
77
+ ## Appeals
78
+
79
+ If you feel a decision was made in error, you may appeal by emailing **conduct-appeal@koadit.com** with:
80
+ - Your appeal details
81
+ - Any additional context
82
+ - Reason why you believe the decision should be reconsidered
83
+
84
+ ## Attribution
85
+
86
+ This Code of Conduct is adapted from:
87
+ - [Contributor Covenant](https://www.contributor-covenant.org/)
88
+ - [Fedora Community Code of Conduct](https://docs.fedoraproject.org/en-US/fedora/latest/code-of-conduct/)
89
+
90
+ ## Questions?
91
+
92
+ If you have any questions about this Code of Conduct, please reach out to **conduct@koadit.com**.
93
+
94
+ ---
95
+
96
+ Thank you for helping create a positive and inclusive community!