@xivdyetools/core 1.3.7

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.
Files changed (103) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +400 -0
  3. package/dist/constants/index.d.ts +56 -0
  4. package/dist/constants/index.d.ts.map +1 -0
  5. package/dist/constants/index.js +103 -0
  6. package/dist/constants/index.js.map +1 -0
  7. package/dist/data/colors_xiv.json +3130 -0
  8. package/dist/data/locales/de.json +231 -0
  9. package/dist/data/locales/en.json +231 -0
  10. package/dist/data/locales/fr.json +231 -0
  11. package/dist/data/locales/ja.json +231 -0
  12. package/dist/data/locales/ko.json +233 -0
  13. package/dist/data/locales/zh.json +233 -0
  14. package/dist/data/presets.json +390 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +18 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/services/APIService.d.ts +246 -0
  20. package/dist/services/APIService.d.ts.map +1 -0
  21. package/dist/services/APIService.js +499 -0
  22. package/dist/services/APIService.js.map +1 -0
  23. package/dist/services/ColorService.d.ts +146 -0
  24. package/dist/services/ColorService.d.ts.map +1 -0
  25. package/dist/services/ColorService.js +209 -0
  26. package/dist/services/ColorService.js.map +1 -0
  27. package/dist/services/DyeService.d.ts +230 -0
  28. package/dist/services/DyeService.d.ts.map +1 -0
  29. package/dist/services/DyeService.js +326 -0
  30. package/dist/services/DyeService.js.map +1 -0
  31. package/dist/services/LocalizationService.d.ts +338 -0
  32. package/dist/services/LocalizationService.d.ts.map +1 -0
  33. package/dist/services/LocalizationService.js +449 -0
  34. package/dist/services/LocalizationService.js.map +1 -0
  35. package/dist/services/PaletteService.d.ts +137 -0
  36. package/dist/services/PaletteService.d.ts.map +1 -0
  37. package/dist/services/PaletteService.js +349 -0
  38. package/dist/services/PaletteService.js.map +1 -0
  39. package/dist/services/PresetService.d.ts +196 -0
  40. package/dist/services/PresetService.d.ts.map +1 -0
  41. package/dist/services/PresetService.js +261 -0
  42. package/dist/services/PresetService.js.map +1 -0
  43. package/dist/services/color/ColorAccessibility.d.ts +39 -0
  44. package/dist/services/color/ColorAccessibility.d.ts.map +1 -0
  45. package/dist/services/color/ColorAccessibility.js +71 -0
  46. package/dist/services/color/ColorAccessibility.js.map +1 -0
  47. package/dist/services/color/ColorConverter.d.ts +146 -0
  48. package/dist/services/color/ColorConverter.d.ts.map +1 -0
  49. package/dist/services/color/ColorConverter.js +393 -0
  50. package/dist/services/color/ColorConverter.js.map +1 -0
  51. package/dist/services/color/ColorManipulator.d.ts +36 -0
  52. package/dist/services/color/ColorManipulator.d.ts.map +1 -0
  53. package/dist/services/color/ColorManipulator.js +56 -0
  54. package/dist/services/color/ColorManipulator.js.map +1 -0
  55. package/dist/services/color/ColorblindnessSimulator.d.ts +35 -0
  56. package/dist/services/color/ColorblindnessSimulator.d.ts.map +1 -0
  57. package/dist/services/color/ColorblindnessSimulator.js +110 -0
  58. package/dist/services/color/ColorblindnessSimulator.js.map +1 -0
  59. package/dist/services/dye/DyeDatabase.d.ts +131 -0
  60. package/dist/services/dye/DyeDatabase.d.ts.map +1 -0
  61. package/dist/services/dye/DyeDatabase.js +367 -0
  62. package/dist/services/dye/DyeDatabase.js.map +1 -0
  63. package/dist/services/dye/DyeSearch.d.ts +55 -0
  64. package/dist/services/dye/DyeSearch.d.ts.map +1 -0
  65. package/dist/services/dye/DyeSearch.js +196 -0
  66. package/dist/services/dye/DyeSearch.js.map +1 -0
  67. package/dist/services/dye/HarmonyGenerator.d.ts +110 -0
  68. package/dist/services/dye/HarmonyGenerator.d.ts.map +1 -0
  69. package/dist/services/dye/HarmonyGenerator.js +221 -0
  70. package/dist/services/dye/HarmonyGenerator.js.map +1 -0
  71. package/dist/services/localization/LocaleLoader.d.ts +38 -0
  72. package/dist/services/localization/LocaleLoader.d.ts.map +1 -0
  73. package/dist/services/localization/LocaleLoader.js +83 -0
  74. package/dist/services/localization/LocaleLoader.js.map +1 -0
  75. package/dist/services/localization/LocaleRegistry.d.ts +73 -0
  76. package/dist/services/localization/LocaleRegistry.d.ts.map +1 -0
  77. package/dist/services/localization/LocaleRegistry.js +84 -0
  78. package/dist/services/localization/LocaleRegistry.js.map +1 -0
  79. package/dist/services/localization/TranslationProvider.d.ts +157 -0
  80. package/dist/services/localization/TranslationProvider.d.ts.map +1 -0
  81. package/dist/services/localization/TranslationProvider.js +289 -0
  82. package/dist/services/localization/TranslationProvider.js.map +1 -0
  83. package/dist/types/index.d.ts +409 -0
  84. package/dist/types/index.d.ts.map +1 -0
  85. package/dist/types/index.js +87 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/dist/types/logger.d.ts +84 -0
  88. package/dist/types/logger.d.ts.map +1 -0
  89. package/dist/types/logger.js +54 -0
  90. package/dist/types/logger.js.map +1 -0
  91. package/dist/utils/index.d.ts +441 -0
  92. package/dist/utils/index.d.ts.map +1 -0
  93. package/dist/utils/index.js +577 -0
  94. package/dist/utils/index.js.map +1 -0
  95. package/dist/utils/kd-tree.d.ts +76 -0
  96. package/dist/utils/kd-tree.d.ts.map +1 -0
  97. package/dist/utils/kd-tree.js +195 -0
  98. package/dist/utils/kd-tree.js.map +1 -0
  99. package/dist/version.d.ts +11 -0
  100. package/dist/version.d.ts.map +1 -0
  101. package/dist/version.js +11 -0
  102. package/dist/version.js.map +1 -0
  103. package/package.json +84 -0
@@ -0,0 +1,326 @@
1
+ /**
2
+ * @xivdyetools/core - Dye Service
3
+ *
4
+ * FFXIV dye database management and search.
5
+ * Provides access to the complete FFXIV dye database with search,
6
+ * matching, and color harmony generation capabilities.
7
+ *
8
+ * Per R-4: Facade class that delegates to focused service classes
9
+ * Maintains backward compatibility while using split services internally
10
+ *
11
+ * Environment-agnostic (Node.js + Browser).
12
+ *
13
+ * @module services/DyeService
14
+ * @example
15
+ * ```typescript
16
+ * import { DyeService, dyeDatabase } from '@xivdyetools/core';
17
+ *
18
+ * const dyeService = new DyeService(dyeDatabase);
19
+ *
20
+ * // Find closest dye to a color
21
+ * const closestDye = dyeService.findClosestDye('#FF0000');
22
+ *
23
+ * // Generate color harmonies
24
+ * const harmonies = dyeService.findTriadicDyes('#FF0000');
25
+ * ```
26
+ */
27
+ import { NoOpLogger } from '../types/index.js';
28
+ import { DyeDatabase } from './dye/DyeDatabase.js';
29
+ import { DyeSearch } from './dye/DyeSearch.js';
30
+ import { HarmonyGenerator } from './dye/HarmonyGenerator.js';
31
+ import { LocalizationService } from './LocalizationService.js';
32
+ /**
33
+ * Service for managing FFXIV dye database (Facade)
34
+ * Per R-4: Delegates to focused service classes for better separation of concerns
35
+ * Maintains backward compatibility with existing API
36
+ *
37
+ * @example
38
+ * // Node.js
39
+ * import { DyeService, dyeDatabase } from '@xivdyetools/core';
40
+ * const dyeService = new DyeService(dyeDatabase);
41
+ *
42
+ * // Browser (Vite auto-imports JSON)
43
+ * import { DyeService, dyeDatabase } from '@xivdyetools/core';
44
+ * const dyeService = new DyeService(dyeDatabase);
45
+ *
46
+ * // With custom logger
47
+ * import { ConsoleLogger } from '@xivdyetools/core';
48
+ * const dyeService = new DyeService(dyeDatabase, { logger: ConsoleLogger });
49
+ */
50
+ export class DyeService {
51
+ /**
52
+ * Initialize the dye database
53
+ * @param dyeData - Array of dyes or JSON object with dye array
54
+ * @param options - Optional configuration including logger
55
+ */
56
+ constructor(dyeData, options = {}) {
57
+ const logger = options.logger ?? NoOpLogger;
58
+ this.database = new DyeDatabase({ logger });
59
+ this.search = new DyeSearch(this.database);
60
+ this.harmony = new HarmonyGenerator(this.database, this.search);
61
+ if (dyeData) {
62
+ this.database.initialize(dyeData);
63
+ }
64
+ }
65
+ // ============================================================================
66
+ // Database Access (delegated to DyeDatabase)
67
+ // ============================================================================
68
+ /**
69
+ * Get all dyes (defensive copy)
70
+ */
71
+ getAllDyes() {
72
+ return this.database.getAllDyes();
73
+ }
74
+ /**
75
+ * Get dye by ID
76
+ */
77
+ getDyeById(id) {
78
+ return this.database.getDyeById(id);
79
+ }
80
+ /**
81
+ * Get multiple dyes by IDs
82
+ */
83
+ getDyesByIds(ids) {
84
+ return this.database.getDyesByIds(ids);
85
+ }
86
+ /**
87
+ * Check if database is loaded
88
+ */
89
+ isLoadedStatus() {
90
+ return this.database.isLoadedStatus();
91
+ }
92
+ /**
93
+ * Get timestamp of last load
94
+ */
95
+ getLastLoadedTime() {
96
+ return this.database.getLastLoadedTime();
97
+ }
98
+ /**
99
+ * Get total dye count
100
+ */
101
+ getDyeCount() {
102
+ return this.database.getDyeCount();
103
+ }
104
+ /**
105
+ * Get all unique categories
106
+ */
107
+ getCategories() {
108
+ return this.database.getCategories();
109
+ }
110
+ // ============================================================================
111
+ // Search & Filter (delegated to DyeSearch)
112
+ // ============================================================================
113
+ /**
114
+ * Search dyes by name (case-insensitive, partial match)
115
+ */
116
+ searchByName(query) {
117
+ return this.search.searchByName(query);
118
+ }
119
+ /**
120
+ * Search dyes by category
121
+ */
122
+ searchByCategory(category) {
123
+ return this.search.searchByCategory(category);
124
+ }
125
+ /**
126
+ * Filter dyes with optional exclusion list
127
+ */
128
+ filterDyes(filter = {}) {
129
+ return this.search.filterDyes(filter);
130
+ }
131
+ /**
132
+ * Find closest dye to a given hex color
133
+ * Per P-7: Uses k-d tree for O(log n) average case vs O(n) linear search
134
+ */
135
+ findClosestDye(hex, excludeIds = []) {
136
+ return this.search.findClosestDye(hex, excludeIds);
137
+ }
138
+ /**
139
+ * Find dyes within a color distance threshold
140
+ * Per P-7: Uses k-d tree for efficient range queries
141
+ */
142
+ findDyesWithinDistance(hex, maxDistance, limit) {
143
+ return this.search.findDyesWithinDistance(hex, maxDistance, limit);
144
+ }
145
+ /**
146
+ * Get dyes sorted by brightness
147
+ */
148
+ getDyesSortedByBrightness(ascending = true) {
149
+ return this.search.getDyesSortedByBrightness(ascending);
150
+ }
151
+ /**
152
+ * Get dyes sorted by saturation
153
+ */
154
+ getDyesSortedBySaturation(ascending = true) {
155
+ return this.search.getDyesSortedBySaturation(ascending);
156
+ }
157
+ /**
158
+ * Get dyes sorted by hue
159
+ */
160
+ getDyesSortedByHue(ascending = true) {
161
+ return this.search.getDyesSortedByHue(ascending);
162
+ }
163
+ // ============================================================================
164
+ // Harmony & Palette Generation (delegated to HarmonyGenerator)
165
+ // ============================================================================
166
+ /**
167
+ * Find dyes that form a complementary color pair
168
+ */
169
+ findComplementaryPair(hex) {
170
+ return this.harmony.findComplementaryPair(hex);
171
+ }
172
+ /**
173
+ * Find analogous dyes (adjacent on color wheel)
174
+ * Returns dyes at ±angle degrees from the base color
175
+ */
176
+ findAnalogousDyes(hex, angle = 30) {
177
+ return this.harmony.findAnalogousDyes(hex, angle);
178
+ }
179
+ /**
180
+ * Find triadic color scheme (colors 120° apart on color wheel)
181
+ */
182
+ findTriadicDyes(hex) {
183
+ return this.harmony.findTriadicDyes(hex);
184
+ }
185
+ /**
186
+ * Find square color scheme (colors 90° apart on color wheel)
187
+ */
188
+ findSquareDyes(hex) {
189
+ return this.harmony.findSquareDyes(hex);
190
+ }
191
+ /**
192
+ * Find tetradic color scheme (two complementary pairs)
193
+ */
194
+ findTetradicDyes(hex) {
195
+ return this.harmony.findTetradicDyes(hex);
196
+ }
197
+ /**
198
+ * Find monochromatic dyes (same hue, varying saturation/brightness)
199
+ */
200
+ findMonochromaticDyes(hex, limit = 6) {
201
+ return this.harmony.findMonochromaticDyes(hex, limit);
202
+ }
203
+ /**
204
+ * Find compound harmony (analogous + complementary)
205
+ */
206
+ findCompoundDyes(hex) {
207
+ return this.harmony.findCompoundDyes(hex);
208
+ }
209
+ /**
210
+ * Find split-complementary harmony (±30° from the complementary hue)
211
+ */
212
+ findSplitComplementaryDyes(hex) {
213
+ return this.harmony.findSplitComplementaryDyes(hex);
214
+ }
215
+ /**
216
+ * Find shades (similar tones, ±15°)
217
+ */
218
+ findShadesDyes(hex) {
219
+ return this.harmony.findShadesDyes(hex);
220
+ }
221
+ // ============================================================================
222
+ // Localization Support (NEW)
223
+ // ============================================================================
224
+ /**
225
+ * Search dyes by name (searches both English + localized names)
226
+ * If no locale is loaded, falls back to English-only search
227
+ *
228
+ * @param query - Search query
229
+ * @returns Matching dyes
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * await LocalizationService.setLocale('ja');
234
+ * const results = dyeService.searchByLocalizedName('スノウ');
235
+ * // Finds "Snow White" (スノウホワイト)
236
+ * ```
237
+ */
238
+ searchByLocalizedName(query) {
239
+ if (!LocalizationService.isLocaleLoaded()) {
240
+ return this.searchByName(query); // Fallback to English-only
241
+ }
242
+ const lowerQuery = query.toLowerCase().trim();
243
+ const dyes = this.database.getDyesInternal();
244
+ return dyes.filter((dye) => {
245
+ // Search English name
246
+ if (dye.name.toLowerCase().includes(lowerQuery)) {
247
+ return true;
248
+ }
249
+ // Search localized name
250
+ const localizedName = LocalizationService.getDyeName(dye.itemID);
251
+ if (localizedName?.toLowerCase().includes(lowerQuery)) {
252
+ return true;
253
+ }
254
+ return false;
255
+ });
256
+ }
257
+ /**
258
+ * Get dye by ID with localized name
259
+ * Returns Dye with localizedName property if locale loaded
260
+ *
261
+ * @param id - Dye ID
262
+ * @returns Localized dye or null if not found
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const dye = dyeService.getLocalizedDyeById(5729);
267
+ * const displayName = dye.localizedName || dye.name;
268
+ * // "スノウホワイト" (ja) or "Snow White" (en)
269
+ * ```
270
+ */
271
+ getLocalizedDyeById(id) {
272
+ const dye = this.getDyeById(id);
273
+ if (!dye)
274
+ return null;
275
+ if (!LocalizationService.isLocaleLoaded()) {
276
+ return dye;
277
+ }
278
+ return {
279
+ ...dye,
280
+ localizedName: LocalizationService.getDyeName(dye.itemID) || undefined,
281
+ };
282
+ }
283
+ /**
284
+ * Get all dyes with localized names
285
+ * Returns array of dyes with localizedName property if locale loaded
286
+ *
287
+ * @returns Array of localized dyes
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * await LocalizationService.setLocale('ja');
292
+ * const dyes = dyeService.getAllLocalizedDyes();
293
+ * dyes.forEach(dye => {
294
+ * console.log(dye.localizedName || dye.name);
295
+ * });
296
+ * ```
297
+ */
298
+ getAllLocalizedDyes() {
299
+ const dyes = this.getAllDyes();
300
+ if (!LocalizationService.isLocaleLoaded()) {
301
+ return dyes;
302
+ }
303
+ return dyes.map((dye) => ({
304
+ ...dye,
305
+ localizedName: LocalizationService.getDyeName(dye.itemID) || undefined,
306
+ }));
307
+ }
308
+ /**
309
+ * Get all non-metallic dyes (locale-aware)
310
+ * Excludes dyes based on metallic dye IDs from current locale
311
+ *
312
+ * @returns Array of non-metallic dyes
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * // Works in any locale - correctly excludes metallic dyes
317
+ * const nonMetallic = dyeService.getNonMetallicDyes();
318
+ * // Excludes: Metallic Silver, Metallic Brass, etc.
319
+ * ```
320
+ */
321
+ getNonMetallicDyes() {
322
+ const metallicIds = new Set(LocalizationService.getMetallicDyeIds());
323
+ return this.getAllDyes().filter((dye) => !metallicIds.has(dye.itemID));
324
+ }
325
+ }
326
+ //# sourceMappingURL=DyeService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DyeService.js","sourceRoot":"","sources":["../../src/services/DyeService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,338 @@
1
+ /**
2
+ * LocalizationService - Facade for localization functionality
3
+ *
4
+ * Per R-4: Facade pattern - delegates to focused service classes
5
+ * Provides multi-language support with lazy loading and fallback chains
6
+ *
7
+ * @module services
8
+ */
9
+ import type { LocaleCode, TranslationKey, HarmonyTypeKey, VisionType, JobKey, GrandCompanyKey, LocalePreference } from '../types/index.js';
10
+ import { LocaleLoader } from './localization/LocaleLoader.js';
11
+ import { LocaleRegistry } from './localization/LocaleRegistry.js';
12
+ import { TranslationProvider } from './localization/TranslationProvider.js';
13
+ /**
14
+ * List of supported locale codes
15
+ */
16
+ export declare const SUPPORTED_LOCALES: readonly LocaleCode[];
17
+ /**
18
+ * Extract 2-letter locale code from longer locale strings
19
+ * Pure function - no side effects, easy to test
20
+ *
21
+ * @param locale - Locale string (e.g., 'en-US', 'ja', 'de-DE')
22
+ * @returns 2-letter locale code or null
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * extractLocaleCode('en-US') // 'en'
27
+ * extractLocaleCode('ja') // 'ja'
28
+ * extractLocaleCode('zh-CN') // null (not supported)
29
+ * ```
30
+ */
31
+ export declare function extractLocaleCode(locale: string): LocaleCode | null;
32
+ /**
33
+ * Resolve locale from preference chain
34
+ * Pure function - determines locale based on priority without side effects
35
+ *
36
+ * Priority: explicit > guild > system > fallback
37
+ *
38
+ * @param preference - Locale preference with fallback chain
39
+ * @returns Resolved locale code
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * resolveLocaleFromPreference({
44
+ * explicit: 'de',
45
+ * guild: 'ja',
46
+ * system: 'en-US',
47
+ * fallback: 'en'
48
+ * }); // Returns 'de' (highest priority)
49
+ *
50
+ * resolveLocaleFromPreference({
51
+ * explicit: null,
52
+ * guild: 'ja-JP',
53
+ * system: 'en',
54
+ * fallback: 'en'
55
+ * }); // Returns 'ja' (extracted from guild)
56
+ * ```
57
+ */
58
+ export declare function resolveLocaleFromPreference(preference: LocalePreference): LocaleCode;
59
+ /**
60
+ * Configuration for LocalizationService dependencies
61
+ */
62
+ export interface LocalizationServiceConfig {
63
+ loader?: LocaleLoader;
64
+ registry?: LocaleRegistry;
65
+ translator?: TranslationProvider;
66
+ }
67
+ /**
68
+ * LocalizationService - Main entry point for localization features
69
+ *
70
+ * Refactored for testability: Supports dependency injection of loader, registry, and translator
71
+ *
72
+ * @example Basic usage (static API)
73
+ * ```typescript
74
+ * import { LocalizationService } from 'xivdyetools-core';
75
+ *
76
+ * // Set locale (lazy loads locale data)
77
+ * await LocalizationService.setLocale('ja');
78
+ *
79
+ * // Get localized label
80
+ * const label = LocalizationService.getLabel('dye'); // "カララント:"
81
+ *
82
+ * // Get localized dye name
83
+ * const dyeName = LocalizationService.getDyeName(5729); // "スノウホワイト"
84
+ * ```
85
+ *
86
+ * @example Instance-based usage for testing
87
+ * ```typescript
88
+ * const mockLoader = new MockLocaleLoader();
89
+ * const service = new LocalizationService({ loader: mockLoader });
90
+ * await service.setLocale('ja');
91
+ * ```
92
+ *
93
+ * @example Discord bot locale resolution
94
+ * ```typescript
95
+ * const preference: LocalePreference = {
96
+ * explicit: interaction.options.getString('language'),
97
+ * guild: interaction.guildLocale,
98
+ * system: interaction.locale,
99
+ * fallback: 'en'
100
+ * };
101
+ * await LocalizationService.setLocaleFromPreference(preference);
102
+ * ```
103
+ */
104
+ export declare class LocalizationService {
105
+ private readonly loader;
106
+ private readonly registry;
107
+ private readonly translator;
108
+ private currentLocale;
109
+ private isInitialized;
110
+ private static defaultInstance;
111
+ /**
112
+ * Constructor with optional dependency injection
113
+ * @param config Configuration with optional loader, registry, and translator
114
+ */
115
+ constructor(config?: LocalizationServiceConfig);
116
+ /**
117
+ * Get the default singleton instance
118
+ * Per Issue #6: Returns eagerly-initialized instance to avoid race conditions
119
+ */
120
+ private static getDefault;
121
+ /**
122
+ * Set active locale (loads locale file if not cached)
123
+ *
124
+ * @param locale - Locale code ('en', 'ja', 'de', 'fr')
125
+ * @throws {AppError} If locale file fails to load
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * await service.setLocale('ja');
130
+ * console.log(service.getCurrentLocale()); // 'ja'
131
+ * ```
132
+ */
133
+ setLocale(locale: LocaleCode): Promise<void>;
134
+ /**
135
+ * Static method: Set locale using default instance
136
+ */
137
+ static setLocale(locale: LocaleCode): Promise<void>;
138
+ /**
139
+ * Set locale from preference object with priority chain
140
+ * Uses pure function resolveLocaleFromPreference for resolution logic
141
+ *
142
+ * Priority: explicit > guild > system > fallback
143
+ *
144
+ * @param preference - Locale preference with fallback chain
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * // Discord bot usage
149
+ * await service.setLocaleFromPreference({
150
+ * explicit: 'de', // User selected German
151
+ * guild: 'ja', // Server is Japanese
152
+ * system: 'en-US', // User's system is English
153
+ * fallback: 'en'
154
+ * });
155
+ * // Sets locale to 'de' (highest priority)
156
+ * ```
157
+ */
158
+ setLocaleFromPreference(preference: LocalePreference): Promise<void>;
159
+ /**
160
+ * Static method: Set locale from preference using default instance
161
+ */
162
+ static setLocaleFromPreference(preference: LocalePreference): Promise<void>;
163
+ /**
164
+ * Get current active locale
165
+ *
166
+ * @returns Current locale code
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * console.log(service.getCurrentLocale()); // 'ja'
171
+ * ```
172
+ */
173
+ getCurrentLocale(): LocaleCode;
174
+ /**
175
+ * Static method: Get current locale from default instance
176
+ */
177
+ static getCurrentLocale(): LocaleCode;
178
+ /**
179
+ * Check if service is initialized with a locale
180
+ *
181
+ * @returns true if locale data is loaded
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * if (!service.isLocaleLoaded()) {
186
+ * await service.setLocale('en');
187
+ * }
188
+ * ```
189
+ */
190
+ isLocaleLoaded(): boolean;
191
+ /**
192
+ * Static method: Check if locale is loaded in default instance
193
+ */
194
+ static isLocaleLoaded(): boolean;
195
+ /**
196
+ * Get localized UI label (with fallback to English)
197
+ *
198
+ * @param key - Translation key
199
+ * @returns Localized label
200
+ */
201
+ getLabel(key: TranslationKey): string;
202
+ /**
203
+ * Static method: Get localized label using default instance
204
+ */
205
+ static getLabel(key: TranslationKey): string;
206
+ /**
207
+ * Get localized dye name by itemID
208
+ *
209
+ * @param itemID - Dye item ID (5729-48227)
210
+ * @returns Localized name or null if not found
211
+ */
212
+ getDyeName(itemID: number): string | null;
213
+ /**
214
+ * Static method: Get localized dye name using default instance
215
+ */
216
+ static getDyeName(itemID: number): string | null;
217
+ /**
218
+ * Get localized category name
219
+ *
220
+ * @param category - Category key (e.g., "Reds", "Blues")
221
+ * @returns Localized category name
222
+ */
223
+ getCategory(category: string): string;
224
+ /**
225
+ * Static method: Get localized category using default instance
226
+ */
227
+ static getCategory(category: string): string;
228
+ /**
229
+ * Get localized acquisition method
230
+ *
231
+ * @param acquisition - Acquisition key (e.g., "Dye Vendor", "Crafting")
232
+ * @returns Localized acquisition method
233
+ */
234
+ getAcquisition(acquisition: string): string;
235
+ /**
236
+ * Static method: Get localized acquisition using default instance
237
+ */
238
+ static getAcquisition(acquisition: string): string;
239
+ /**
240
+ * Get all metallic dye IDs (for exclusion filtering)
241
+ *
242
+ * @returns Array of metallic dye item IDs
243
+ */
244
+ getMetallicDyeIds(): number[];
245
+ /**
246
+ * Static method: Get metallic dye IDs using default instance
247
+ */
248
+ static getMetallicDyeIds(): number[];
249
+ /**
250
+ * Get localized harmony type name
251
+ *
252
+ * @param key - Harmony type key
253
+ * @returns Localized harmony type
254
+ */
255
+ getHarmonyType(key: HarmonyTypeKey): string;
256
+ /**
257
+ * Static method: Get localized harmony type using default instance
258
+ */
259
+ static getHarmonyType(key: HarmonyTypeKey): string;
260
+ /**
261
+ * Get localized vision type name
262
+ *
263
+ * @param key - Vision type key
264
+ * @returns Localized vision type
265
+ */
266
+ getVisionType(key: VisionType): string;
267
+ /**
268
+ * Static method: Get localized vision type using default instance
269
+ */
270
+ static getVisionType(key: VisionType): string;
271
+ /**
272
+ * Get localized job name
273
+ *
274
+ * @param key - Job key
275
+ * @returns Localized job name
276
+ */
277
+ getJobName(key: JobKey): string;
278
+ /**
279
+ * Static method: Get localized job name using default instance
280
+ */
281
+ static getJobName(key: JobKey): string;
282
+ /**
283
+ * Get localized Grand Company name
284
+ *
285
+ * @param key - Grand Company key
286
+ * @returns Localized Grand Company name
287
+ */
288
+ getGrandCompanyName(key: GrandCompanyKey): string;
289
+ /**
290
+ * Static method: Get localized Grand Company name using default instance
291
+ */
292
+ static getGrandCompanyName(key: GrandCompanyKey): string;
293
+ /**
294
+ * Get all available locale codes
295
+ *
296
+ * @returns Array of supported locale codes
297
+ */
298
+ getAvailableLocales(): LocaleCode[];
299
+ /**
300
+ * Static method: Get available locales
301
+ */
302
+ static getAvailableLocales(): LocaleCode[];
303
+ /**
304
+ * Preload multiple locales for instant switching
305
+ * Useful for apps that need to switch locales without delay
306
+ *
307
+ * @param locales - Array of locale codes to preload
308
+ */
309
+ preloadLocales(locales: LocaleCode[]): Promise<void>;
310
+ /**
311
+ * Static method: Preload locales using default instance
312
+ */
313
+ static preloadLocales(locales: LocaleCode[]): Promise<void>;
314
+ /**
315
+ * Clear all loaded locales from cache
316
+ * Useful for testing or memory management
317
+ */
318
+ clear(): void;
319
+ /**
320
+ * Static method: Clear cache of default instance
321
+ */
322
+ static clear(): void;
323
+ /**
324
+ * Reset the static singleton instance
325
+ * Useful for testing to prevent test pollution between test suites
326
+ * Per Issue #6: Creates a fresh instance since we use eager initialization
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * // In test cleanup
331
+ * afterEach(() => {
332
+ * LocalizationService.resetInstance();
333
+ * });
334
+ * ```
335
+ */
336
+ static resetInstance(): void;
337
+ }
338
+ //# sourceMappingURL=LocalizationService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizationService.d.ts","sourceRoot":"","sources":["../../src/services/LocalizationService.ts"],"names":[],"mappings":"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"}