@kopynator/angular 1.0.14 → 1.0.15

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/README.md CHANGED
@@ -92,6 +92,24 @@ O con header/selector:
92
92
  <button (click)="kopyService.setLocale('es')">Español</button>
93
93
  ```
94
94
 
95
+ ### SSR: idioma guardado sin parpadeo
96
+
97
+ Al cambiar de idioma se guarda en `localStorage` y en la cookie `kopy_locale`. Para que el **servidor** pinte ya con ese idioma (y no se vea español → idioma guardado), proporciona `KOPY_INITIAL_LOCALE` desde la request en `app.config.server.ts`:
98
+
99
+ ```ts
100
+ import { inject, REQUEST } from '@angular/core';
101
+ import { KOPY_INITIAL_LOCALE, getKopyLocaleFromCookieHeader } from '@kopynator/angular';
102
+
103
+ // en providers del server:
104
+ {
105
+ provide: KOPY_INITIAL_LOCALE,
106
+ useFactory: () => {
107
+ const req = inject(REQUEST, { optional: true }) as { headers?: { cookie?: string } } | null;
108
+ return req?.headers?.cookie ? getKopyLocaleFromCookieHeader(req.headers.cookie) : null;
109
+ }
110
+ }
111
+ ```
112
+
95
113
  ### (Opcional) Componente \<kopy-ready\>
96
114
 
97
115
  Solo si necesitas ocultar un bloque concreto hasta que estén listas las traducciones, puedes usar `<kopy-ready>`. En la mayoría de proyectos **no es necesario**; el `APP_INITIALIZER` carga las traducciones antes del bootstrap (igual que ngx-translate), con o sin SSR.
@@ -1,23 +1,46 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, Inject, Injectable, Pipe, effect, Input, Directive, inject, Component, input, provideAppInitializer } from '@angular/core';
2
+ import { InjectionToken, signal, Inject, Optional, Injectable, Pipe, effect, Input, Directive, inject, Component, input, provideAppInitializer } from '@angular/core';
3
3
  import { Kopynator } from '@kopynator/core';
4
4
  import * as i1 from '@angular/common';
5
5
  import { CommonModule } from '@angular/common';
6
6
 
7
7
  const KOPY_CONFIG = new InjectionToken('KOPY_CONFIG');
8
+ /** In SSR: provide this from the request cookie so the first paint uses the user's saved locale (no flicker). */
9
+ const KOPY_INITIAL_LOCALE = new InjectionToken('KOPY_INITIAL_LOCALE');
10
+ const COOKIE_NAME = 'kopy_locale';
11
+ const COOKIE_MAX_AGE = 365 * 24 * 60 * 60; // 1 year
12
+ /** Parse Cookie header (e.g. from req.headers.cookie) and return the value of kopy_locale, or null. */
13
+ function getKopyLocaleFromCookieHeader(cookieHeader) {
14
+ if (!cookieHeader || typeof cookieHeader !== 'string')
15
+ return null;
16
+ const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${COOKIE_NAME}=([^;]*)`));
17
+ return match ? decodeURIComponent(match[1].trim()) : null;
18
+ }
19
+ function readCookieFromDocument() {
20
+ if (typeof document === 'undefined')
21
+ return null;
22
+ return getKopyLocaleFromCookieHeader(document.cookie);
23
+ }
24
+ function setCookieInDocument(locale) {
25
+ if (typeof document === 'undefined')
26
+ return;
27
+ document.cookie = `${COOKIE_NAME}=${encodeURIComponent(locale)};path=/;max-age=${COOKIE_MAX_AGE};SameSite=Lax`;
28
+ }
8
29
  class KopyService {
9
30
  config;
10
31
  zone;
11
32
  kopy;
12
- // Signals for reactivity
13
33
  isReady = signal(false, ...(ngDevMode ? [{ debugName: "isReady" }] : []));
14
34
  availableLanguages = signal([], ...(ngDevMode ? [{ debugName: "availableLanguages" }] : []));
15
35
  currentLocale = signal('en', ...(ngDevMode ? [{ debugName: "currentLocale" }] : []));
16
- constructor(config, zone) {
36
+ constructor(config, zone, serverLocale) {
17
37
  this.config = config;
18
38
  this.zone = zone;
19
- const savedLocale = typeof localStorage !== 'undefined' ? localStorage.getItem('kopy_locale') : null;
20
- const initialLocale = savedLocale || config.defaultLocale || 'en';
39
+ const initialLocale = serverLocale ??
40
+ readCookieFromDocument() ??
41
+ (typeof localStorage !== 'undefined' ? localStorage.getItem(COOKIE_NAME) : null) ??
42
+ config.defaultLocale ??
43
+ 'en';
21
44
  this.kopy = new Kopynator({ ...config, defaultLocale: initialLocale });
22
45
  this.currentLocale.set(initialLocale);
23
46
  }
@@ -31,8 +54,9 @@ class KopyService {
31
54
  async setLocale(locale) {
32
55
  await this.kopy.setLocale(locale);
33
56
  if (typeof localStorage !== 'undefined') {
34
- localStorage.setItem('kopy_locale', locale);
57
+ localStorage.setItem(COOKIE_NAME, locale);
35
58
  }
59
+ setCookieInDocument(locale);
36
60
  this.zone.run(() => {
37
61
  this.currentLocale.set(locale);
38
62
  });
@@ -43,7 +67,7 @@ class KopyService {
43
67
  getKopynator() {
44
68
  return this.kopy;
45
69
  }
46
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: KopyService, deps: [{ token: KOPY_CONFIG }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
70
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: KopyService, deps: [{ token: KOPY_CONFIG }, { token: i0.NgZone }, { token: KOPY_INITIAL_LOCALE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
47
71
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: KopyService, providedIn: 'root' });
48
72
  }
49
73
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: KopyService, decorators: [{
@@ -54,7 +78,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
54
78
  }], ctorParameters: () => [{ type: undefined, decorators: [{
55
79
  type: Inject,
56
80
  args: [KOPY_CONFIG]
57
- }] }, { type: i0.NgZone }] });
81
+ }] }, { type: i0.NgZone }, { type: undefined, decorators: [{
82
+ type: Optional
83
+ }, {
84
+ type: Inject,
85
+ args: [KOPY_INITIAL_LOCALE]
86
+ }] }] });
58
87
 
59
88
  class KopyPipe {
60
89
  kopyService;
@@ -279,5 +308,5 @@ function provideKopynator(config) {
279
308
  * Generated bundle index. Do not edit.
280
309
  */
281
310
 
282
- export { KOPY_CONFIG, KopyDirective, KopyPipe, KopyReadyComponent, KopySelectorComponent, KopyService, provideKopynator };
311
+ export { KOPY_CONFIG, KOPY_INITIAL_LOCALE, KopyDirective, KopyPipe, KopyReadyComponent, KopySelectorComponent, KopyService, getKopyLocaleFromCookieHeader, provideKopynator };
283
312
  //# sourceMappingURL=kopynator-angular.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"kopynator-angular.mjs","sources":["../../../../packages/angular/src/lib/kopy.service.ts","../../../../packages/angular/src/lib/kopy.pipe.ts","../../../../packages/angular/src/lib/kopy.directive.ts","../../../../packages/angular/src/lib/kopy.selector.component.ts","../../../../packages/angular/src/lib/kopy-ready.component.ts","../../../../packages/angular/src/public-api.ts","../../../../packages/angular/src/kopynator-angular.ts"],"sourcesContent":["import { Injectable, Inject, InjectionToken, signal, NgZone } from '@angular/core';\nimport { Kopynator, KopyConfig } from '@kopynator/core';\n\nexport const KOPY_CONFIG = new InjectionToken<KopyConfig>('KOPY_CONFIG');\n\n@Injectable({\n providedIn: 'root'\n})\nexport class KopyService {\n private kopy: Kopynator;\n\n // Signals for reactivity\n public isReady = signal(false);\n public availableLanguages = signal<string[]>([]);\n public currentLocale = signal<string>('en');\n\n constructor(\n @Inject(KOPY_CONFIG) private config: KopyConfig,\n private zone: NgZone\n ) {\n const savedLocale = typeof localStorage !== 'undefined' ? localStorage.getItem('kopy_locale') : null;\n const initialLocale = savedLocale || config.defaultLocale || 'en';\n\n this.kopy = new Kopynator({ ...config, defaultLocale: initialLocale });\n this.currentLocale.set(initialLocale);\n }\n\n public async init(): Promise<void> {\n await this.kopy.init();\n this.zone.run(() => {\n this.availableLanguages.set(this.kopy.getLanguages());\n this.isReady.set(true);\n });\n }\n\n async setLocale(locale: string) {\n await this.kopy.setLocale(locale);\n\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem('kopy_locale', locale);\n }\n\n this.zone.run(() => {\n this.currentLocale.set(locale);\n });\n }\n\n translate(key: string, params: Record<string, any> = {}): string {\n return this.kopy.translate(key, params);\n }\n\n getKopynator(): Kopynator {\n return this.kopy;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { KopyService } from './kopy.service';\n\n@Pipe({\n name: 'kopy',\n pure: false, // Must be false to react to signal changes inside transform\n standalone: true\n})\nexport class KopyPipe implements PipeTransform {\n constructor(\n private kopyService: KopyService\n ) {}\n\n transform(key: string, params: Record<string, any> = {}): string {\n const ready = this.kopyService.isReady();\n this.kopyService.currentLocale(); // reactive dependency\n if (!ready) return '';\n\n return this.kopyService.translate(key, params);\n }\n}\n","import { Directive, ElementRef, Input, OnChanges, SimpleChanges, effect } from '@angular/core';\nimport { KopyService } from './kopy.service';\n\n@Directive({\n selector: '[kopy]',\n standalone: true\n})\nexport class KopyDirective implements OnChanges {\n @Input('kopy') key!: string;\n @Input() kopyParams: Record<string, any> = {};\n\n constructor(\n private el: ElementRef,\n private kopyService: KopyService\n ) {\n // React to signal changes in KopyService\n effect(() => {\n this.kopyService.currentLocale();\n this.kopyService.isReady();\n this.render();\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n this.render();\n }\n\n private render() {\n if (!this.key) return;\n if (!this.kopyService.isReady()) {\n this.el.nativeElement.textContent = '';\n return;\n }\n this.el.nativeElement.textContent = this.kopyService.translate(this.key, this.kopyParams);\n }\n}\n","import { Component, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { KopyService } from './kopy.service';\n\n@Component({\n selector: 'kopy-selector',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"kopy-selector-container\" [class.open]=\"isOpen()\">\n <button class=\"kopy-selected-btn\" (click)=\"toggleDropdown()\">\n <span class=\"flag\">{{ getFlag(currentLocale()) }}</span>\n <span class=\"label uppercase\">{{ currentLocale() }}</span>\n <svg class=\"chevron\" [class.rotate]=\"isOpen()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n\n <div class=\"kopy-dropdown\" *ngIf=\"isOpen()\">\n <button \n *ngFor=\"let lang of languages()\" \n class=\"kopy-dropdown-item\"\n [class.active]=\"lang === currentLocale()\"\n (click)=\"selectLanguage(lang)\"\n >\n <span class=\"flag\">{{ getFlag(lang) }}</span>\n <span class=\"label\">{{ getNativeName(lang) }}</span>\n </button>\n </div>\n </div>\n `,\n styles: [`\n .kopy-selector-container {\n position: relative;\n display: inline-block;\n font-family: inherit;\n }\n .kopy-selected-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 99px;\n color: white;\n cursor: pointer;\n transition: all 0.2s;\n font-size: 11px;\n font-weight: 700;\n min-width: 70px;\n justify-content: center;\n }\n .kopy-selected-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n border-color: rgba(255, 255, 255, 0.2);\n }\n .chevron {\n width: 10px;\n height: 10px;\n opacity: 0.5;\n transition: transform 0.2s;\n }\n .chevron.rotate {\n transform: rotate(180deg);\n }\n .kopy-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 8px;\n background: #1e293b;\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 12px;\n box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);\n overflow: hidden;\n min-width: 140px;\n z-index: 100;\n animation: slideIn 0.2s ease-out;\n }\n .kopy-dropdown-item {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 16px;\n background: transparent;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n text-align: left;\n transition: all 0.2s;\n font-size: 14px;\n }\n .kopy-dropdown-item:hover {\n background: rgba(255, 255, 255, 0.05);\n color: white;\n }\n .kopy-dropdown-item.active {\n color: #38bdf8;\n background: rgba(56, 189, 248, 0.05);\n }\n .label {\n flex: 1;\n }\n .flag {\n font-size: 16px;\n }\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(-10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `]\n})\nexport class KopySelectorComponent {\n private kopyService = inject(KopyService);\n \n public isOpen = signal(false);\n public languages = this.kopyService.availableLanguages;\n public currentLocale = this.kopyService.currentLocale;\n\n private flagMap: Record<string, string> = {\n 'en': '🇺🇸',\n 'es': '🇪🇸',\n 'fr': '🇫🇷',\n 'de': '🇩🇪',\n 'it': '🇮🇹',\n 'pt': '🇵🇹',\n 'ja': '🇯🇵',\n 'zh': '🇨🇳',\n 'ru': '🇷🇺'\n };\n\n private nameMap: Record<string, string> = {\n 'en': 'English',\n 'es': 'Español',\n 'fr': 'Français',\n 'de': 'Deutsch',\n 'it': 'Italiano',\n 'pt': 'Português',\n 'ja': '日本語',\n 'zh': '中文',\n 'ru': 'Русский',\n 'en-us': 'English (US)'\n };\n\n toggleDropdown() {\n this.isOpen.set(!this.isOpen());\n }\n\n selectLanguage(lang: string) {\n this.kopyService.setLocale(lang);\n this.isOpen.set(false);\n }\n\n getFlag(lang: string): string {\n const code = lang.split('-')[0].toLowerCase();\n return this.flagMap[code] || '🌐';\n }\n\n getNativeName(lang: string): string {\n return this.nameMap[lang.toLowerCase()] || lang.toUpperCase();\n }\n}\n","import { Component, inject, input } from '@angular/core';\nimport { KopyService } from './kopy.service';\n\n/**\n * Envuelve el contenido y solo lo muestra cuando las traducciones están cargadas (isReady).\n * Evita el parpadeo de claves (FOUT) sin tener que usar señales ni afterNextRender en la app.\n *\n * Uso en app.html:\n * <kopy-ready>\n * <router-outlet></router-outlet>\n * </kopy-ready>\n *\n * Opcional: showLoader=false para no mostrar spinner (solo espacio vacío hasta que cargue).\n */\n@Component({\n selector: 'kopy-ready',\n standalone: true,\n template: `\n @if (kopyService.isReady()) {\n <ng-content></ng-content>\n } @else if (showLoader()) {\n <div class=\"kopy-ready-loading\" aria-busy=\"true\" aria-label=\"Cargando traducciones\">\n <span class=\"kopy-ready-spinner\" aria-hidden=\"true\"></span>\n </div>\n }\n `,\n styles: [`\n .kopy-ready-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 60vh;\n }\n .kopy-ready-spinner {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n border: 2px solid rgba(6, 182, 212, 0.3);\n border-top-color: rgb(6, 182, 212);\n border-radius: 50%;\n animation: kopy-spin 0.7s linear infinite;\n }\n @keyframes kopy-spin {\n to { transform: rotate(360deg); }\n }\n `]\n})\nexport class KopyReadyComponent {\n readonly kopyService = inject(KopyService);\n /** Si true (por defecto), muestra un spinner mientras cargan las traducciones. */\n readonly showLoader = input<boolean>(true);\n}\n","import { Provider, provideAppInitializer, inject, EnvironmentProviders } from '@angular/core';\nimport { KopyConfig } from '@kopynator/core';\nimport { KOPY_CONFIG, KopyService } from './lib/kopy.service';\n\nexport * from './lib/kopy.service';\nexport * from './lib/kopy.pipe';\nexport * from './lib/kopy.directive';\nexport * from './lib/kopy.selector.component';\nexport * from './lib/kopy-ready.component';\n\n/**\n * Provides the Kopynator SDK configuration to the application.\n * Like ngx-translate: translations are loaded in APP_INITIALIZER before the app\n * bootstraps, so no wrapper component is needed and the pipe never shows raw keys.\n */\nexport function provideKopynator(config: KopyConfig): (Provider | EnvironmentProviders)[] {\n return [\n {\n provide: KOPY_CONFIG,\n useValue: config\n },\n KopyService,\n provideAppInitializer(() => {\n const kopyService = inject(KopyService);\n return kopyService.init();\n })\n ];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.KopyService"],"mappings":"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"}
1
+ {"version":3,"file":"kopynator-angular.mjs","sources":["../../../../packages/angular/src/lib/kopy.service.ts","../../../../packages/angular/src/lib/kopy.pipe.ts","../../../../packages/angular/src/lib/kopy.directive.ts","../../../../packages/angular/src/lib/kopy.selector.component.ts","../../../../packages/angular/src/lib/kopy-ready.component.ts","../../../../packages/angular/src/public-api.ts","../../../../packages/angular/src/kopynator-angular.ts"],"sourcesContent":["import { Injectable, Inject, InjectionToken, signal, NgZone, Optional } from '@angular/core';\nimport { Kopynator, KopyConfig } from '@kopynator/core';\n\nexport const KOPY_CONFIG = new InjectionToken<KopyConfig>('KOPY_CONFIG');\n\n/** In SSR: provide this from the request cookie so the first paint uses the user's saved locale (no flicker). */\nexport const KOPY_INITIAL_LOCALE = new InjectionToken<string | null>('KOPY_INITIAL_LOCALE');\n\nconst COOKIE_NAME = 'kopy_locale';\nconst COOKIE_MAX_AGE = 365 * 24 * 60 * 60; // 1 year\n\n/** Parse Cookie header (e.g. from req.headers.cookie) and return the value of kopy_locale, or null. */\nexport function getKopyLocaleFromCookieHeader(cookieHeader: string | undefined): string | null {\n if (!cookieHeader || typeof cookieHeader !== 'string') return null;\n const match = cookieHeader.match(new RegExp(`(?:^|;\\\\s*)${COOKIE_NAME}=([^;]*)`));\n return match ? decodeURIComponent(match[1].trim()) : null;\n}\n\nfunction readCookieFromDocument(): string | null {\n if (typeof document === 'undefined') return null;\n return getKopyLocaleFromCookieHeader(document.cookie);\n}\n\nfunction setCookieInDocument(locale: string): void {\n if (typeof document === 'undefined') return;\n document.cookie = `${COOKIE_NAME}=${encodeURIComponent(locale)};path=/;max-age=${COOKIE_MAX_AGE};SameSite=Lax`;\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class KopyService {\n private kopy: Kopynator;\n\n public isReady = signal(false);\n public availableLanguages = signal<string[]>([]);\n public currentLocale = signal<string>('en');\n\n constructor(\n @Inject(KOPY_CONFIG) private config: KopyConfig,\n private zone: NgZone,\n @Optional() @Inject(KOPY_INITIAL_LOCALE) serverLocale: string | null\n ) {\n const initialLocale =\n serverLocale ??\n readCookieFromDocument() ??\n (typeof localStorage !== 'undefined' ? localStorage.getItem(COOKIE_NAME) : null) ??\n config.defaultLocale ??\n 'en';\n\n this.kopy = new Kopynator({ ...config, defaultLocale: initialLocale });\n this.currentLocale.set(initialLocale);\n }\n\n public async init(): Promise<void> {\n await this.kopy.init();\n this.zone.run(() => {\n this.availableLanguages.set(this.kopy.getLanguages());\n this.isReady.set(true);\n });\n }\n\n async setLocale(locale: string) {\n await this.kopy.setLocale(locale);\n\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(COOKIE_NAME, locale);\n }\n setCookieInDocument(locale);\n\n this.zone.run(() => {\n this.currentLocale.set(locale);\n });\n }\n\n translate(key: string, params: Record<string, any> = {}): string {\n return this.kopy.translate(key, params);\n }\n\n getKopynator(): Kopynator {\n return this.kopy;\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { KopyService } from './kopy.service';\n\n@Pipe({\n name: 'kopy',\n pure: false, // Must be false to react to signal changes inside transform\n standalone: true\n})\nexport class KopyPipe implements PipeTransform {\n constructor(\n private kopyService: KopyService\n ) {}\n\n transform(key: string, params: Record<string, any> = {}): string {\n const ready = this.kopyService.isReady();\n this.kopyService.currentLocale(); // reactive dependency\n if (!ready) return '';\n\n return this.kopyService.translate(key, params);\n }\n}\n","import { Directive, ElementRef, Input, OnChanges, SimpleChanges, effect } from '@angular/core';\nimport { KopyService } from './kopy.service';\n\n@Directive({\n selector: '[kopy]',\n standalone: true\n})\nexport class KopyDirective implements OnChanges {\n @Input('kopy') key!: string;\n @Input() kopyParams: Record<string, any> = {};\n\n constructor(\n private el: ElementRef,\n private kopyService: KopyService\n ) {\n // React to signal changes in KopyService\n effect(() => {\n this.kopyService.currentLocale();\n this.kopyService.isReady();\n this.render();\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n this.render();\n }\n\n private render() {\n if (!this.key) return;\n if (!this.kopyService.isReady()) {\n this.el.nativeElement.textContent = '';\n return;\n }\n this.el.nativeElement.textContent = this.kopyService.translate(this.key, this.kopyParams);\n }\n}\n","import { Component, inject, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { KopyService } from './kopy.service';\n\n@Component({\n selector: 'kopy-selector',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"kopy-selector-container\" [class.open]=\"isOpen()\">\n <button class=\"kopy-selected-btn\" (click)=\"toggleDropdown()\">\n <span class=\"flag\">{{ getFlag(currentLocale()) }}</span>\n <span class=\"label uppercase\">{{ currentLocale() }}</span>\n <svg class=\"chevron\" [class.rotate]=\"isOpen()\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n\n <div class=\"kopy-dropdown\" *ngIf=\"isOpen()\">\n <button \n *ngFor=\"let lang of languages()\" \n class=\"kopy-dropdown-item\"\n [class.active]=\"lang === currentLocale()\"\n (click)=\"selectLanguage(lang)\"\n >\n <span class=\"flag\">{{ getFlag(lang) }}</span>\n <span class=\"label\">{{ getNativeName(lang) }}</span>\n </button>\n </div>\n </div>\n `,\n styles: [`\n .kopy-selector-container {\n position: relative;\n display: inline-block;\n font-family: inherit;\n }\n .kopy-selected-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 99px;\n color: white;\n cursor: pointer;\n transition: all 0.2s;\n font-size: 11px;\n font-weight: 700;\n min-width: 70px;\n justify-content: center;\n }\n .kopy-selected-btn:hover {\n background: rgba(255, 255, 255, 0.1);\n border-color: rgba(255, 255, 255, 0.2);\n }\n .chevron {\n width: 10px;\n height: 10px;\n opacity: 0.5;\n transition: transform 0.2s;\n }\n .chevron.rotate {\n transform: rotate(180deg);\n }\n .kopy-dropdown {\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 8px;\n background: #1e293b;\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 12px;\n box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);\n overflow: hidden;\n min-width: 140px;\n z-index: 100;\n animation: slideIn 0.2s ease-out;\n }\n .kopy-dropdown-item {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 16px;\n background: transparent;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n text-align: left;\n transition: all 0.2s;\n font-size: 14px;\n }\n .kopy-dropdown-item:hover {\n background: rgba(255, 255, 255, 0.05);\n color: white;\n }\n .kopy-dropdown-item.active {\n color: #38bdf8;\n background: rgba(56, 189, 248, 0.05);\n }\n .label {\n flex: 1;\n }\n .flag {\n font-size: 16px;\n }\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(-10px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `]\n})\nexport class KopySelectorComponent {\n private kopyService = inject(KopyService);\n \n public isOpen = signal(false);\n public languages = this.kopyService.availableLanguages;\n public currentLocale = this.kopyService.currentLocale;\n\n private flagMap: Record<string, string> = {\n 'en': '🇺🇸',\n 'es': '🇪🇸',\n 'fr': '🇫🇷',\n 'de': '🇩🇪',\n 'it': '🇮🇹',\n 'pt': '🇵🇹',\n 'ja': '🇯🇵',\n 'zh': '🇨🇳',\n 'ru': '🇷🇺'\n };\n\n private nameMap: Record<string, string> = {\n 'en': 'English',\n 'es': 'Español',\n 'fr': 'Français',\n 'de': 'Deutsch',\n 'it': 'Italiano',\n 'pt': 'Português',\n 'ja': '日本語',\n 'zh': '中文',\n 'ru': 'Русский',\n 'en-us': 'English (US)'\n };\n\n toggleDropdown() {\n this.isOpen.set(!this.isOpen());\n }\n\n selectLanguage(lang: string) {\n this.kopyService.setLocale(lang);\n this.isOpen.set(false);\n }\n\n getFlag(lang: string): string {\n const code = lang.split('-')[0].toLowerCase();\n return this.flagMap[code] || '🌐';\n }\n\n getNativeName(lang: string): string {\n return this.nameMap[lang.toLowerCase()] || lang.toUpperCase();\n }\n}\n","import { Component, inject, input } from '@angular/core';\nimport { KopyService } from './kopy.service';\n\n/**\n * Envuelve el contenido y solo lo muestra cuando las traducciones están cargadas (isReady).\n * Evita el parpadeo de claves (FOUT) sin tener que usar señales ni afterNextRender en la app.\n *\n * Uso en app.html:\n * <kopy-ready>\n * <router-outlet></router-outlet>\n * </kopy-ready>\n *\n * Opcional: showLoader=false para no mostrar spinner (solo espacio vacío hasta que cargue).\n */\n@Component({\n selector: 'kopy-ready',\n standalone: true,\n template: `\n @if (kopyService.isReady()) {\n <ng-content></ng-content>\n } @else if (showLoader()) {\n <div class=\"kopy-ready-loading\" aria-busy=\"true\" aria-label=\"Cargando traducciones\">\n <span class=\"kopy-ready-spinner\" aria-hidden=\"true\"></span>\n </div>\n }\n `,\n styles: [`\n .kopy-ready-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 60vh;\n }\n .kopy-ready-spinner {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n border: 2px solid rgba(6, 182, 212, 0.3);\n border-top-color: rgb(6, 182, 212);\n border-radius: 50%;\n animation: kopy-spin 0.7s linear infinite;\n }\n @keyframes kopy-spin {\n to { transform: rotate(360deg); }\n }\n `]\n})\nexport class KopyReadyComponent {\n readonly kopyService = inject(KopyService);\n /** Si true (por defecto), muestra un spinner mientras cargan las traducciones. */\n readonly showLoader = input<boolean>(true);\n}\n","import { Provider, provideAppInitializer, inject, EnvironmentProviders } from '@angular/core';\nimport { KopyConfig } from '@kopynator/core';\nimport { KOPY_CONFIG, KopyService } from './lib/kopy.service';\n\nexport * from './lib/kopy.service';\nexport * from './lib/kopy.pipe';\nexport * from './lib/kopy.directive';\nexport * from './lib/kopy.selector.component';\nexport * from './lib/kopy-ready.component';\n\n/**\n * Provides the Kopynator SDK configuration to the application.\n * Like ngx-translate: translations are loaded in APP_INITIALIZER before the app\n * bootstraps, so no wrapper component is needed and the pipe never shows raw keys.\n */\nexport function provideKopynator(config: KopyConfig): (Provider | EnvironmentProviders)[] {\n return [\n {\n provide: KOPY_CONFIG,\n useValue: config\n },\n KopyService,\n provideAppInitializer(() => {\n const kopyService = inject(KopyService);\n return kopyService.init();\n })\n ];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.KopyService"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kopynator/angular",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -3,6 +3,10 @@ import { InjectionToken, NgZone, PipeTransform, OnChanges, ElementRef, SimpleCha
3
3
  import { KopyConfig, Kopynator } from '@kopynator/core';
4
4
 
5
5
  declare const KOPY_CONFIG: InjectionToken<KopyConfig>;
6
+ /** In SSR: provide this from the request cookie so the first paint uses the user's saved locale (no flicker). */
7
+ declare const KOPY_INITIAL_LOCALE: InjectionToken<string>;
8
+ /** Parse Cookie header (e.g. from req.headers.cookie) and return the value of kopy_locale, or null. */
9
+ declare function getKopyLocaleFromCookieHeader(cookieHeader: string | undefined): string | null;
6
10
  declare class KopyService {
7
11
  private config;
8
12
  private zone;
@@ -10,12 +14,12 @@ declare class KopyService {
10
14
  isReady: i0.WritableSignal<boolean>;
11
15
  availableLanguages: i0.WritableSignal<string[]>;
12
16
  currentLocale: i0.WritableSignal<string>;
13
- constructor(config: KopyConfig, zone: NgZone);
17
+ constructor(config: KopyConfig, zone: NgZone, serverLocale: string | null);
14
18
  init(): Promise<void>;
15
19
  setLocale(locale: string): Promise<void>;
16
20
  translate(key: string, params?: Record<string, any>): string;
17
21
  getKopynator(): Kopynator;
18
- static ɵfac: i0.ɵɵFactoryDeclaration<KopyService, never>;
22
+ static ɵfac: i0.ɵɵFactoryDeclaration<KopyService, [null, null, { optional: true; }]>;
19
23
  static ɵprov: i0.ɵɵInjectableDeclaration<KopyService>;
20
24
  }
21
25
 
@@ -80,4 +84,4 @@ declare class KopyReadyComponent {
80
84
  */
81
85
  declare function provideKopynator(config: KopyConfig): (Provider | EnvironmentProviders)[];
82
86
 
83
- export { KOPY_CONFIG, KopyDirective, KopyPipe, KopyReadyComponent, KopySelectorComponent, KopyService, provideKopynator };
87
+ export { KOPY_CONFIG, KOPY_INITIAL_LOCALE, KopyDirective, KopyPipe, KopyReadyComponent, KopySelectorComponent, KopyService, getKopyLocaleFromCookieHeader, provideKopynator };