1+ // año del footer
12const year = document . getElementById ( 'year' ) ;
23if ( year ) year . textContent = new Date ( ) . getFullYear ( ) ;
34
5+ // estado de idioma
46const STATE = { lang : localStorage . getItem ( 'lang' ) || 'es' , dict : { } } ;
57
8+ // carga JSON de idioma
69async function loadDict ( lang ) {
7- const res = await fetch ( `assets/i18n/${ lang } .json` ) ;
8- STATE . dict = await res . json ( ) ;
9- STATE . lang = lang ;
10- localStorage . setItem ( 'lang' , lang ) ;
11- applyI18n ( ) ;
10+ try {
11+ const res = await fetch ( `assets/i18n/${ lang } .json` ) ;
12+ const json = await res . json ( ) ;
13+ STATE . dict = json ;
14+ STATE . lang = lang ;
15+ localStorage . setItem ( 'lang' , lang ) ;
16+ applyI18n ( ) ;
17+ } catch ( e ) {
18+ console . error ( 'Error cargando diccionario' , e ) ;
19+ }
1220}
1321
22+ // aplica textos + re-renderiza secciones
1423function applyI18n ( ) {
15- if ( ! STATE . dict || ! STATE . dict . nav ) return ; // aún no se ha cargado el JSON
24+ if ( ! STATE . dict || ! STATE . dict . nav ) {
25+ // aún no se ha cargado bien el JSON
26+ return ;
27+ }
1628
1729 document . querySelectorAll ( '[data-i18n]' ) . forEach ( el => {
1830 const key = el . getAttribute ( 'data-i18n' ) ;
1931 const val = key . split ( '.' ) . reduce ( ( o , k ) => o ?. [ k ] , STATE . dict ) ;
2032 if ( typeof val === 'string' ) el . textContent = val ;
2133 } ) ;
34+
2235 renderSkills ( ) ;
2336 renderProjects ( ) ;
2437 renderXP ( ) ;
2538 renderServices ( ) ;
2639 document . documentElement . lang = STATE . lang ;
2740}
2841
29-
42+ // toggle ES/EN
3043document . getElementById ( 'lang-toggle' ) ?. addEventListener ( 'click' , ( ) => {
3144 loadDict ( STATE . lang === 'es' ? 'en' : 'es' ) ;
3245} ) ;
3346
34-
47+ // === RENDER PROYECTOS ===
3548function renderProjects ( ) {
3649 const grid = document . getElementById ( "projects-grid" ) ;
37- if ( ! grid || ! STATE . dict || ! STATE . dict . projects ) return ;
50+ if ( ! grid ) return ;
51+ if ( ! STATE . dict || ! STATE . dict . projects ) {
52+ console . warn ( 'Projects aún no disponible en STATE.dict' ) ;
53+ return ;
54+ }
3855
3956 const proj = STATE . dict . projects ;
4057
@@ -82,28 +99,17 @@ function renderProjects(){
8299 ` ) . join ( "" ) ;
83100}
84101
85-
102+ // === RENDER SKILLS ===
86103function renderSkills ( ) {
87104 const grid = document . getElementById ( "skills-grid" ) ;
88105 if ( ! grid || ! STATE . dict . skills ) return ;
89106
107+ const s = STATE . dict . skills ;
90108 const blocks = [
91- {
92- title : STATE . dict . skills . dev_title ,
93- items : STATE . dict . skills . dev_items
94- } ,
95- {
96- title : STATE . dict . skills . web_title ,
97- items : STATE . dict . skills . web_items
98- } ,
99- {
100- title : STATE . dict . skills . data_title ,
101- items : STATE . dict . skills . data_items
102- } ,
103- {
104- title : STATE . dict . skills . soft_title ,
105- items : STATE . dict . skills . soft_items
106- }
109+ { title : s . dev_title , items : s . dev_items } ,
110+ { title : s . web_title , items : s . web_items } ,
111+ { title : s . data_title , items : s . data_items } ,
112+ { title : s . soft_title , items : s . soft_items }
107113 ] ;
108114
109115 grid . innerHTML = blocks . map ( b => `
@@ -116,23 +122,16 @@ function renderSkills(){
116122 ` ) . join ( "" ) ;
117123}
118124
125+ // === RENDER EXPERIENCIA ===
119126function renderXP ( ) {
120127 const container = document . getElementById ( "xp-list" ) ;
121128 if ( ! container || ! STATE . dict . xp ) return ;
122129
130+ const x = STATE . dict . xp ;
123131 const items = [
124- {
125- title : STATE . dict . xp . xp1_title ,
126- body : STATE . dict . xp . xp1_body
127- } ,
128- {
129- title : STATE . dict . xp . xp2_title ,
130- body : STATE . dict . xp . xp2_body
131- } ,
132- {
133- title : STATE . dict . xp . xp3_title ,
134- body : STATE . dict . xp . xp3_body
135- }
132+ { title : x . xp1_title , body : x . xp1_body } ,
133+ { title : x . xp2_title , body : x . xp2_body } ,
134+ { title : x . xp3_title , body : x . xp3_body }
136135 ] ;
137136
138137 container . innerHTML = items . map ( i => `
@@ -141,30 +140,18 @@ function renderXP(){
141140 <p>${ i . body } </p>
142141 </div>
143142 ` ) . join ( "" ) ;
144-
145- const certs = document . getElementById ( "services-list" ) ;
146143}
147144
145+ // === RENDER SERVICIOS ===
148146function renderServices ( ) {
149147 const container = document . getElementById ( "services-list" ) ;
150148 if ( ! container || ! STATE . dict . services ) return ;
151149
150+ const s = STATE . dict . services ;
152151 const items = [
153- {
154- icon : "⚙️" ,
155- title : STATE . dict . services . s1_title ,
156- body : STATE . dict . services . s1_body
157- } ,
158- {
159- icon : "🖥️" ,
160- title : STATE . dict . services . s2_title ,
161- body : STATE . dict . services . s2_body
162- } ,
163- {
164- icon : "🛠️" ,
165- title : STATE . dict . services . s3_title ,
166- body : STATE . dict . services . s3_body
167- }
152+ { icon : "⚙️" , title : s . s1_title , body : s . s1_body } ,
153+ { icon : "🖥️" , title : s . s2_title , body : s . s2_body } ,
154+ { icon : "🛠️" , title : s . s3_title , body : s . s3_body }
168155 ] ;
169156
170157 container . innerHTML = items . map ( i => `
@@ -176,6 +163,7 @@ function renderServices(){
176163 ` ) . join ( "" ) ;
177164}
178165
166+ // === ANIMACIÓN DE SECCIONES ===
179167function setupSectionObserver ( ) {
180168 const sections = document . querySelectorAll ( '.section' ) ;
181169 const observer = new IntersectionObserver ( entries => {
@@ -185,15 +173,13 @@ function setupSectionObserver(){
185173 observer . unobserve ( entry . target ) ;
186174 }
187175 } ) ;
188- } , {
189- threshold : 0.18
190- } ) ;
176+ } , { threshold : 0.18 } ) ;
191177
192178 sections . forEach ( sec => observer . observe ( sec ) ) ;
193179}
194180
181+ // arranque
195182document . addEventListener ( "DOMContentLoaded" , ( ) => {
196- loadDict ( STATE . lang ) ;
197183 setupSectionObserver ( ) ;
184+ loadDict ( STATE . lang ) ;
198185} ) ;
199-
0 commit comments