Преглед на файлове

1.语言切换就当前页面,不跳转首页;2.cookie记录用户当前选择语言

guoziyun преди 1 година
родител
ревизия
19e4186f4a
променени са 24 файла, в които са добавени 578 реда и са изтрити 240 реда
  1. 6 2
      app.js
  2. 86 68
      config/meta.js
  3. 43 25
      config/translate.js
  4. 21 0
      package-lock.json
  5. 1 0
      package.json
  6. 125 30
      routes/index.js
  7. 67 0
      test/language.html
  8. 3 2
      views/album-section.ejs
  9. 6 4
      views/album.ejs
  10. 1 1
      views/albums.ejs
  11. 6 6
      views/banner.ejs
  12. 10 9
      views/category.ejs
  13. 22 0
      views/designer-section.ejs
  14. 15 7
      views/designer.ejs
  15. 1 1
      views/designers.ejs
  16. 51 33
      views/detail.ejs
  17. 54 11
      views/header.ejs
  18. 10 6
      views/hot-section.ejs
  19. 1 0
      views/index.ejs
  20. 2 1
      views/latest-section.ejs
  21. 7 7
      views/search.ejs
  22. 10 6
      views/special-section.ejs
  23. 8 7
      views/special.ejs
  24. 22 14
      views/tag.ejs

+ 6 - 2
app.js

@@ -5,7 +5,7 @@ const config = require('./config/app')
 
 
 const session = require('express-session');
 const session = require('express-session');
 const RedisStore = require('connect-redis')(session);
 const RedisStore = require('connect-redis')(session);
-
+const cookieParser = require('cookie-parser');
 const bodyParser = require('body-parser');
 const bodyParser = require('body-parser');
 const authChecker = require('./libs/auth/checker');
 const authChecker = require('./libs/auth/checker');
 
 
@@ -16,6 +16,8 @@ app.set('view engine', 'ejs');
 app.set('views', path.join(__dirname, 'views'));
 app.set('views', path.join(__dirname, 'views'));
 
 
 
 
+app.use(cookieParser());
+
 app.use(express.static(config.STATIC_DIR));
 app.use(express.static(config.STATIC_DIR));
 app.use(express.static(path.join(__dirname, 'dist')));
 app.use(express.static(path.join(__dirname, 'dist')));
 
 
@@ -49,9 +51,11 @@ app.use('/napi/web/role', authChecker.checkLogin, require('./routes/napi/web/rol
 
 
 app.use('/thumbs/v1', require('./routes/res/thumbs'));
 app.use('/thumbs/v1', require('./routes/res/thumbs'));
 
 
+app.use('/proxy', require('./routes/proxy'));
+
+
 app.use('/', require('./routes/index'));
 app.use('/', require('./routes/index'));
 
 
-app.use('/proxy', require('./routes/proxy'));
 
 
 
 
 // catch 404 and forward to error handler
 // catch 404 and forward to error handler

+ 86 - 68
config/meta.js

@@ -1,49 +1,49 @@
 let homePageTile = {
 let homePageTile = {
-  zh: '数字填色',
-  en: 'Art Coloring Pages',
-  es: 'Páginas de coloreo artístico',
-  pt: 'Páginas de colorir artísticas',
-  ja: 'アート塗り絵用紙',
+  zh: '免费可打印涂色页 | 在线数字填色',
+  en: 'Free printable coloring pages | Paint by number online',
+  es: 'Páginas de colorear gratuitas e imprimibles | Pintar por número en línea',
+  pt: 'Páginas de colorir gratuitas e imprimíveis | Pintar por número online',
+  ja: '無料で印刷可能な塗り絵ページ | オンライン数字ペイント',
 }
 }
 
 
 let homePageDescription = {
 let homePageDescription = {
-  zh: 'art.pcoloring.com 是涂画家的乐园,海量原创的、免费的填色图,等着您来给它们涂上颜色。如果你愿意,你可以把它们下载并打印出来,根据自己的喜好着色。当然你也可以在线进行着色游戏,不需要复杂的操作,只需要根据数字的指引,即可完成一幅幅精美绝伦的作品,让你感受关于构图和色彩的艺术气息...',
-  en: `art.pcoloring.com is a paradise for coloring enthusiasts, offering a vast collection of original and free coloring pages waiting for you to bring them to life with your colors. If you prefer, you can download and print them out to color according to your own preferences. Alternatively, you can enjoy online coloring games without any complicated steps—just follow the number guide to complete stunning works of art, immersing yourself in the artistry of composition and color.`,
-  es: 'art.pcoloring.com es un paraíso para los amantes del coloreado, que ofrece una gran variedad de páginas para colorear originales y gratuitas esperando a que les des vida con tus colores. Si lo prefieres, puedes descargarlas e imprimirlas para colorearlas según tus gustos. También puedes disfrutar de juegos de coloreado en línea sin pasos complicados: solo tienes que seguir la guía de números para completar increíbles obras de arte y sumergirte en el arte de la composición y el color.',
-  pt: 'art.pcoloring.com é um paraíso para os entusiastas de colorir, oferecendo uma enorme coleção de páginas para colorir originais e gratuitas esperando por você para dar-lhes vida com suas cores. Se preferir, você pode baixar e imprimir para colorir de acordo com suas preferências. Você também pode desfrutar de jogos de colorir online sem nenhuma etapa complicada - basta seguir o guia de números para completar obras de arte deslumbrantes, mergulhando na arte da composição e da cor.',
-  ja: 'art.pcoloring.com は涂色愛好家の楽園で、あなたの色で命を吹き込むことを待っているオリジナルかつ無料の涂色ページが山ほどあります。好きなように涂色したい場合は、ダウンロードして印刷することもできます。また、複雑な操作は必要なく、数字のガイドに従うだけで見事なアート作品を完成させることができるオンライン涂色ゲームも楽しめます。構図と色彩の芸術的世界に浸ってください...',
+  zh: '海量填色页,涂画者的乐园,涵盖动物、植物、曼陀罗等各种类型。所有填色页均免费、可打印,你也可以在线进行数字填色。',
+  en: `A vast number of coloring pages, a paradise for colorists, includes various types such as animals, plants, mandalas, and so on. All coloring pages are free, printable, or you can paint by number online.`,
+  es: 'Una gran cantidad de páginas de colorear, un paraíso para los amantes del coloreado, incluye varios tipos como animales, plantas, mandalas, etc. Todas las páginas de colorear son gratuitas, imprimibles o puedes pintar por número en línea.',
+  pt: 'Uma vasta quantidade de páginas de colorir, um paraíso para os amantes de colorir, inclui vários tipos, como animais, plantas, mandalas e assim por diante. Todas as páginas de colorir são gratuitas, imprimíveis ou você pode pintar por número online.',
+  ja: 'たくさんの塗り絵ページ、塗り絵愛好者の天国です。動物、植物、マンダラなど様々な種類が含まれています。すべての塗り絵ページは無料で印刷可能で、あるいはオンラインで数字ペイントをすることもできます。',
 }
 }
 
 
 let categoryTitle = {
 let categoryTitle = {
-  zh: '色页分类',
-  en: 'Coloring Pages Categories',
-  es: 'Categorías de páginas para colorear',
-  pt: 'Categorias de páginas para colorir',
-  ja: '塗り絵用紙のカテゴリー',
+  zh: '色页分类',
+  en: 'Coloring Page Categories',
+  es: 'Categorías de Páginas de Colorear',
+  pt: 'Categorías de Páginas de Colorear',
+  ja: '塗り絵ページのカテゴリー',
 }
 }
 
 
 let categoryDescription = {
 let categoryDescription = {
-  zh: '为您精选组织的热门填色主题分类,包括动物、植物、人物、风景、曼陀罗、建筑、食物、宠物、花鸟鱼虫等等,帮助您快速找到感兴趣的填色页。',
-  en: `We've carefully selected and organized popular coloring themes for you, including animals, plants, people, landscapes, mandalas, architecture, food, pets, flowers, birds, fish, and insects, etc. This helps you quickly find the coloring pages that interest you.`,
-  es: 'Hemos seleccionado y organizado cuidadosamente categorías de temas de coloreo populares para usted, que incluyen animales, plantas, personas, paisajes, mandalas, arquitectura, alimentos, mascotas, flores, pájaros, peces e insectos, etc. Esto le ayudará a encontrar rápidamente las páginas de coloreo que le interesen.',
-  pt: 'Selecionamos e organizamos cuidadosamente categorias de temas de coloração populares para você, incluindo animais, plantas, pessoas, paisagens, mandalas, arquitetura, alimentos, animais de estimação, flores, pássaros, peixes e insetos, etc. Isso ajuda você a encontrar rapidamente as páginas de colorir que lhe interessam.',
-  ja: '当社では、動物、植物、人物、風景、マンダラ、建築、食べ物、ペット、花鳥魚虫などの人気の塗り絵テーマを厳選して分類しています。これにより、あなたが興味のある塗り絵用紙を素早く見つけることができます。',
+  zh: '丰富的填色页分类:动物类填色页、植物类填色页、曼陀罗类填色页、奇幻类填色页等等,帮助您快速找到感兴趣的填色页。',
+  en: `Abundant coloring page categories: Animal category coloring pages, Plant category coloring pages, Mandalas category coloring pages, Fantasy category coloring pages, and so on, help you quickly find the coloring pages that interest you.`,
+  es: 'Abundantes categorías de páginas de colorear: páginas de colorear de la categoría de animales, páginas de colorear de la categoría de plantas, páginas de colorear de la categoría de mandalas, páginas de colorear de la categoría de fantasía, etc., te ayudan a encontrar rápidamente las páginas de colorear que te interesan.',
+  pt: 'Abundantes categorias de páginas de colorir: páginas de colorir da categoria de animais, páginas de colorir da categoria de plantas, páginas de colorir da categoria de mandalas, páginas de colorir da categoria de fantasia, etc., ajudam você a encontrar rapidamente as páginas de colorir que o interessam.',
+  ja: '豊富な塗り絵ページのカテゴリー:動物カテゴリの塗り絵ページ、植物カテゴリの塗り絵ページ、マンダラカテゴリの塗り絵ページ、ファンタジーカテゴリの塗り絵ページなど、あなたが興味のある塗り絵ページを素早く見つけるのに役立ちます。',
 }
 }
 
 
 let tagTitle = {
 let tagTitle = {
   zh: '涂色页标签检索',
   zh: '涂色页标签检索',
-  en: 'Coloring Pages Tags',
-  es: 'Etiquetas de páginas para colorear',
-  pt: 'Marcadores de páginas para colorir',
-  ja: '塗り絵用紙のタグ',
+  en: 'Coloring Page Tags',
+  es: 'Etiquetas de Páginas de Colorear',
+  pt: 'Tags de Páginas de Colorir',
+  ja: '塗り絵ページのタグ',
 }
 }
 
 
 let tagDescription = {
 let tagDescription = {
-  zh: '超乎您想象,多达上百个热门标签:阳光、沙滩、大海、老虎、孔雀、蝴蝶,父母、女孩、婴儿、花园、花鸟鱼虫等等等等,满足您对涂色页面的更细分的检索需求。',
-  en: 'Beyond your imagination, there are up to hundreds of popular tags: sunshine, beach, sea, tiger, peacock, butterfly, parents, girl, baby, garden, flowers, birds, fish, insects and so on. They can meet your more specific search needs for coloring pages.',
-  es: 'Más allá de lo que puedes imaginar, hay hasta cientos de etiquetas populares: sol, playa, mar, tigre, pavo real, mariposa, padres, niña, bebé, jardín, flores, pájaros, peces, insectos, etc. Estas etiquetas pueden satisfacer tus necesidades de búsqueda más detalladas para las páginas de colorear.',
-  pt: 'Além do que você pode imaginar, há até centenas de marcadores populares: sol, praia, mar, tigre, pavão, borboleta, pais, menina, bebê, jardim, flores, pássaros, peixes, insetos, etc. Eles atendem às suas necessidades de busca mais específicas para páginas de colorir.',
-  ja: '想像を超える、太陽、砂浜、海、トラ、孔雀、蝶、両親、少女、赤ちゃん、庭園、花鳥魚虫など、何百もの人気タグがあります。これらはあなたの塗り絵ページに対するより細分化された検索ニーズを満たします。',
+  zh: '数百个热门填色页标签:阳光、海滩、大海、老虎、孔雀、蝴蝶、父母、女孩、婴儿、花园、花朵、鸟类、鱼类、昆虫等等。它们能满足您对填色页更具体的搜索需求。',
+  en: 'Hundreds of popular coloring pages tags: sunshine, beach, sea, tiger, peacock, butterfly, parents, girl, baby, garden, flowers, birds, fish, insects and so on. They can meet your more specific search needs for coloring pages.',
+  es: 'Cientos de populares etiquetas de páginas de colorear: sol, playa, mar, tigre, pavo real, mariposa, padres, chica, bebé, jardín, flores, pájaros, peces, insectos, etc. Pueden satisfacer tus necesidades de búsqueda más específicas de páginas de colorear.',
+  pt: 'Centenas de populares tags de páginas de colorir: sol, praia, mar, tigre, pavão, borboleta, pais, menina, bebê, jardim, flores, pássaros, peixes, insetos, etc. Eles podem atender às suas necessidades de pesquisa mais específicas de páginas de colorir.',
+  ja: '数百の人気のある塗り絵ページのタグ:太陽、ビーチ、海、トラ、孔雀、蝶、両親、女の子、赤ちゃん、庭、花、鳥、魚、昆虫など。これらは、あなたの塗り絵ページに対するより具体的な検索ニーズを満たすことができます。',
 }
 }
 
 
 let searchTitle = {
 let searchTitle = {
@@ -55,28 +55,28 @@ let searchTitle = {
 }
 }
 
 
 let searchDescription = {
 let searchDescription = {
-  zh: '我们提供的热门分类、标签、专辑 还找不到您感兴趣的内容? 那就随心所欲地用关键词搜索你想要的涂色页吧,相信我们的海量图片内容和AI智能检索,定能找到你想要的... ',
-  en: `Can't find what you're interested in from our popular categories, tags, and albums? Then feel free to search for the coloring pages you want using keywords. We're confident that our vast collection of images and AI-powered intelligent search will help you find exactly what you're looking for...`,
-  es: '¿No encuentras el contenido que te interesa entre nuestras categorías populares, etiquetas y álbumes? Entonces, busca las páginas de colorear que quieres usando palabras clave a tu antojo. Estamos seguros de que nuestra inmensa colección de imágenes y la búsqueda inteligente impulsada por IA te ayudarán a encontrar exactamente lo que estás buscando...',
-  pt: 'Não consegue encontrar o que está interessado nas nossas categorias populares, marcadores e álbuns? Então, sinta-se à vontade para pesquisar as páginas de colorir que deseja usando palavras-chave. Temos certeza de que nossa vasta coleção de imagens e a pesquisa inteligente baseada em IA irão ajudá-lo a encontrar exatamente o que está procurando...',
-  ja: '当社が提供する人気のカテゴリ、タグ、アルバムの中から、あなたが興味を持つ内容が見つからないですか?それなら、キーワードを使って思いのままに探したい塗り絵ページを検索してください。私たちの膨大な画像コンテンツと AI による高度な検索機能で、きっとあなたが求めるものを見つけることができると信じています...',
+  zh: '按标签或关键词搜索可打印填色页',
+  en: `Search printable coloring page by tags or keywords`,
+  es: 'Busca páginas de colorear imprimibles por etiquetas o palabras clave',
+  pt: 'Procure páginas de colorir imprimíveis por tags ou palavras - chave',
+  ja: 'タグまたはキーワードで印刷可能な塗り絵ページを検索する',
 }
 }
 
 
 let specialTitle = {
 let specialTitle = {
-  zh: '彩绘 - 特殊涂画作品集',
-  en: 'Special Coloring Page Collection',
-  es: 'Colección especial de páginas para colorear',
-  pt: 'Coleção especial de páginas para colorir',
-  ja: '特別な塗り絵用紙コレクション',
+  zh: '彩绘 - 特殊填色页',
+  en: 'Special Coloring Pages',
+  es: 'Páginas de Colorear Especiales',
+  pt: 'Páginas de Colorir Especiais',
+  ja: '特殊な塗り絵ページ',
 }
 }
 
 
 
 
 let specialDescription = {
 let specialDescription = {
-  zh: '不同于普通的涂色页一个区块只能一个颜色, 彩绘涂色作品拥有更多的颜色空间和细节,可以呈现出类似照片的细腻感和真实感,相信会给你不一样的“惊喜”, 快来试试吧。',
-  en: `Unlike ordinary coloring pages where each section can only have one color, painted coloring works offer more room for colors and details, capable of presenting a delicate and realistic feel similar to that of a photo. We're sure it'll bring you a unique "surprise". Come and give it a try!`,
-  es: 'A diferencia de las páginas de colorear normales en las que cada sección solo puede tener un color, las obras de coloreado tienen más espacio para los colores y detalles, y pueden ofrecer una sensación de suavidad y realismo similar a la de una foto. Estamos seguros de que te dará una “sorpresa” diferente. ¡Ven y pruébalo ahora!',
-  pt: 'Diferentemente das páginas de colorir comuns, nas quais cada seção pode ter apenas uma cor, as obras de colorir pintadas oferecem mais espaço para cores e detalhes, sendo capazes de apresentar uma sensação de delicadeza e realismo semelhante à de uma foto. Temos certeza de que trará uma “surpresa” única para você. Venha e dê uma chance!',
-  ja: '一般的な塗り絵用紙では一つの区画に一色しか塗れませんが、彩り豊かな塗り絵作品は、より多くの色を使った表現と細部描写が可能で、写真のような繊細さとリアリティを演出することができます。きっとあなたに新鮮な「驚き」をもたらしてくれるはずです。ぜひ試してみてください!',
+  zh: '与普通填色页每个区域只能有一种颜色不同,特殊填色页为色彩和细节提供了更多空间,能够呈现出与照片相似的细腻逼真质感。我们确信它会给您带来特别的 “惊喜”。快来试试吧!',
+  en: `Unlike ordinary coloring pages where each area can only have one color, special coloring pages offer more room for colors and details, capable of presenting a delicate and realistic feel similar to that of a photo. We're sure it'll bring you a special "surprise". Come and give it a try!`,
+  es: 'A diferencia de las páginas de colorear comunes en las que cada área solo puede tener un color, las páginas de colorear especiales ofrecen más espacio para los colores y los detalles, y son capaces de presentar una sensación delicada y realista similar a la de una foto. Estamos seguros de que te traerá una sorpresa especial. ¡Ven y dale una oportunidad!',
+  pt: 'Ao contrário das páginas de colorir comuns, nas quais cada área só pode ter uma cor, as páginas de colorir especiais oferecem mais espaço para cores e detalhes, sendo capazes de apresentar uma sensação delicada e realista semelhante à de uma foto. Temos certeza de que trará uma surpresa especial para você. Venha experimentar!',
+  ja: '通常の塗り絵ページでは各エリアに 1 色だけを塗ることができますが、特殊な塗り絵ページは色やディテールの表現にもっと幅を持たせており、写真のように繊細でリアルな感じを表現することができます。きっと特別な「驚き」をあなたにもたらすことでしょう。ぜひ試してみてください!',
 }
 }
 
 
 let albumsTitle = {
 let albumsTitle = {
@@ -88,15 +88,15 @@ let albumsTitle = {
 }
 }
 
 
 let albumsDescription = {
 let albumsDescription = {
-  zh: '我们将持续推出涂色页系列专辑,如圣诞主题、星座主题、可爱宝贝主题等等,根据不同的主题绘制一系列精美的图片,作为精品呈现给您...',
-  en: `We will continuously launch a series of coloring page albums, such as Christmas-themed, constellation-themed, cute baby-themed ones, etc. We'll draw a series of beautiful pictures based on different themes and present them to you as high-quality works...`,
-  es: 'Seguiremos lanzando una serie de álbumes de páginas para colorear, como los de tema navideño, los de tema de constelaciones, los de tema de bebés adorables, etc. Dibujaremos una serie de hermosas imágenes basadas en diferentes temas y se las presentaremos a usted como obras de alta calidad...',
-  pt: 'Vamos continuar lançando uma série de álbuns de páginas para colorir, como os com tema de Natal, os com tema de constelações, os com tema de bebês fofos, etc. Vamos desenhar uma série de belas imagens com base em diferentes temas e apresentá-las a você como obras de alta qualidade...',
-  ja: '当社は塗り絵用紙のシリーズアルバムを継続的にリリースします。例えばクリスマステーマ、星座テーマ、可愛いベビーテーマなど様々なテーマのアルバムを用意します。それぞれのテーマに基づいて、美しい絵を一連に描き、逸品として皆様にお届けします...',
+  zh: '我们为您精心整理了多个填色页专辑。每个专辑都包含一系列相同主题的填色页,比如圣诞老人填色页专辑、欢乐农场填色页专辑、星座女神填色页专辑等等。',
+  en: `We have carefully organized multiple coloring page albums for you. Each album contains a series of coloring pages with the same theme, such as the Santa Claus coloring page album, the Happy Farm coloring page album, the Constellation Goddess coloring page album, and so on.`,
+  es: 'Hemos organizado cuidadosamente múltiples álbumes de páginas de colorear para usted. Cada álbum contiene una serie de páginas de colorear con el mismo tema, como el álbum de páginas de colorear de Santa Claus, el álbum de páginas de colorear de la Granja Feliz, el álbum de páginas de colorear de la Diosa de las Conjuntas, etc.',
+  pt: 'Organizamos cuidadosamente vários álbuns de páginas de colorir para você. Cada álbum contém uma série de páginas de colorir com o mesmo tema, como o álbum de páginas de colorir de Papai Noel, o álbum de páginas de colorir da Fazenda Feliz, o álbum de páginas de colorir da Deusa das Conjuntas, etc.',
+  ja: '当サイトでは、たくさんの塗り絵ページのアルバムを丁寧に整理して提供しています。各アルバムには、サンタクロースの塗り絵ページアルバム、ハッピーファームの塗り絵ページアルバム、星座の女神の塗り絵ページアルバムなど、同じテーマの一連の塗り絵ページが含まれています。',
 }
 }
 
 
 let albumDetailTitle = {
 let albumDetailTitle = {
-  zh: '涂色专辑详情',
+  zh: '涂色专辑详情',
   en: 'Coloring Page Album Details',
   en: 'Coloring Page Album Details',
   es: 'Detalles del álbum de páginas para colorear',
   es: 'Detalles del álbum de páginas para colorear',
   pt: 'Detalhes do álbum de páginas para colorir',
   pt: 'Detalhes do álbum de páginas para colorir',
@@ -111,20 +111,36 @@ let albumDetailDescription = {
   ja: '塗り絵用紙のアルバムの詳細情報を確認し、アルバムの逸品コンテンツを見て、あなたが興味を持つ絵画作品を選び、数字に従って塗り始めましょう...',
   ja: '塗り絵用紙のアルバムの詳細情報を確認し、アルバムの逸品コンテンツを見て、あなたが興味を持つ絵画作品を選び、数字に従って塗り始めましょう...',
 }
 }
 
 
+let designersTitle = {
+  zh: '艺术涂色页设计师',
+  en: 'Art Coloring Page Designers',
+  es: 'Diseñadores de Páginas de Colorear Artísticas',
+  pt: 'Designers de Páginas de Colorir Artísticas',
+  ja: 'アート塗り絵ページデザイナー',
+}
+
+let designersDescription = {
+  zh: 'art.pcoloring.com汇集了众多杰出的艺术填色页设计师。正是因为他们的天才创意,才为我们创作出精彩的填色页作品。快来看看有没有你喜欢的设计师吧。',
+  en: `art.pcoloring.com has gathered numbers of outstanding art coloring page designers. It is precisely because of their genius ideas that they have produced wonderful coloring page works for us. Come and check if there is any designer you like.`,
+  es: 'art.pcoloring.com ha reunido a numerosos excelentes diseñadores de páginas de coloración artística. Es precisamente debido a sus ideas geniales que han creado maravillosos trabajos de páginas de coloración para nosotros. Ven y comprueba si hay algún diseñador que te guste.',
+  pt: 'art.pcoloring.com reuniu numerosos excelentes designers de páginas de coloração artística. É justamente por causa de suas ideias geniais que eles criaram maravilhosas páginas de coloração para nós. Venha ver se há algum designer que você goste.',
+  ja: 'art.pcoloring.comには数多くの優秀なアート塗り絵ページデザイナーが集まっています。まさに彼らの天才的なアイデアのおかげで、私たちに素晴らしい塗り絵ページの作品を生み出しています。あなたの好きなデザイナーがいるか見てみましょう。',
+}
+
 let designerTitle = {
 let designerTitle = {
-  zh: '艺术涂色页设计师专栏',
-  en: 'Art Coloring Page Designer Column',
-  es: 'Columna del diseñador de páginas de arte para colorear',
-  pt: 'Coluna do designer de páginas de arte para colorir',
-  ja: 'アート塗り絵用紙デザイナーコラム',
+  zh: '艺术涂色页设计师',
+  en: 'Art Coloring Page Designer',
+  es: 'Diseñador(a) de páginas de coloración artística',
+  pt: 'Designer de páginas de coloração artística',
+  ja: 'アートクラフト塗り絵ページデザイナー',
 }
 }
 
 
 let designerDescription = {
 let designerDescription = {
-  zh: '你是否曾经对某张艺术涂画作品印象深刻?或许是这这张作品的设计师的审美引起了你的共鸣!那么请移步设计师专栏,查看您感兴趣的设计师的作品合集吧。',
-  en: `Have you ever been deeply impressed by a certain piece of art coloring work? Maybe it's the aesthetic sense of the designer of this work that resonates with you! Then please go to the designer column and check out the collections of works by the designers you're interested in.`,
-  es: '¿Alguna vez te has quedado impresionado por una determinada obra de arte de colorear? Quizás es el sentido estético del diseñador de esta obra el que te ha hecho sentir una conexión! Entonces, ve a la columna de los diseñadores y consulta las colecciones de obras de los diseñadores que te interesen.',
-  pt: 'Você já se impressionou com uma determinada obra de arte de colorir? Talvez seja o senso estético do designer desta obra que ressoa com você! Então, vá para a coluna dos designers e confira as coleções de obras dos designers que lhe interessam.',
-  ja: 'あなたは曾て、あるアート塗り絵作品に深く感銘を受けたことはありませんか?おそらく、その作品のデザイナーの審美眼があなたの共感を呼んでいるのではないでしょうか!では、デザイナーコラムに移動して、あなたが興味を持つデザイナーの作品コレクションをチェックしてみてください。',
+  zh: '填色页设计师作品集,你是否对某个设计师的画作印象深刻,或许你们的审美一致,来看看他的填色页作品吧',
+  en: `Coloring Page Designer's Portfolio. Are you deeply impressed by a certain designer's paintings? Perhaps you share the same aesthetic. Come and take a look at her coloring page works.`,
+  es: 'Portafolio del Diseñador de Páginas de Coloración. ¿Te han impresionado profundamente las pinturas de un determinado diseñador? Quizás compartas el mismo gusto estético. Ven y echa un vistazo a sus trabajos de páginas de coloración.',
+  pt: 'Portfólio do Designer de Páginas de Colorir. Algum desenho de determinado designer te impressionou profundamente? Talvez você tenha o mesmo senso estético. Venha dar uma olhada em suas páginas de colorir.',
+  ja: '塗り絵ページデザイナーの作品集。特定のデザイナーの絵があなたに深い印象を与えましたか?多分あなたたちは同じ美学的感覚を持っています。彼女の塗り絵ページの作品を見てみましょう。',
 }
 }
 
 
 let infoTitle = {
 let infoTitle = {
@@ -145,18 +161,18 @@ let infoDescription = {
 
 
 let detailTitle = {
 let detailTitle = {
   zh: '涂色作品详情',
   zh: '涂色作品详情',
-  en: 'Coloring Works Details',
-  es: 'Detalles de las obras de colorear',
-  pt: 'Detalhes das obras de colorir',
-  ja: '塗り絵作品の詳細',
+  en: 'Coloring Page Details',
+  es: 'Detalles de la Página de Colorear',
+  pt: 'Detalles de la Página de Colorear',
+  ja: '塗り絵ページの詳細',
 }
 }
 
 
 let detailDescription = {
 let detailDescription = {
   zh: '涂色作品详情',
   zh: '涂色作品详情',
-  en: 'Coloring Works Details',
-  es: 'Detalles de las obras de colorear',
-  pt: 'Detalhes das obras de colorir',
-  ja: '塗り絵作品の詳細',
+  en: 'Coloring Page Details',
+  es: 'Detalles de la Página de Colorear',
+  pt: 'Detalles de la Página de Colorear',
+  ja: '塗り絵ページの詳細',
 }
 }
 
 
 let playTitle = {
 let playTitle = {
@@ -193,6 +209,8 @@ let meta = {
   albumsDescription,
   albumsDescription,
   albumDetailTitle,
   albumDetailTitle,
   albumDetailDescription,
   albumDetailDescription,
+  designersTitle,
+  designersDescription,
   designerTitle,
   designerTitle,
   designerDescription,
   designerDescription,
   infoTitle,
   infoTitle,

+ 43 - 25
config/translate.js

@@ -1,17 +1,9 @@
-let titleTest = {
-  zh: '快乐的火鸡',
-  en: 'Happy turkey',
-  es: 'Pavo feliz',
-  pt: 'Peru alegre',
-  ja: '幸せな七面鳥',
-}
-
-let descTest = {
-  zh: '在一个充满秋意的森林里,住着一只快乐的火鸡。它叫小火球,因为它的羽毛就像秋天的枫叶一样火红。感恩节快到了,小火球决定给森林里的朋友们送去祝福。它戴上了一顶漂亮的帽子,精心制作了一块写着 “感恩节快乐” 的牌子,然后兴高采烈地出发了。一路上,它遇到了许多南瓜朋友,它们也都穿着橙色的新衣,仿佛在为节日欢呼。小火球带着满满的热情,将感恩节的快乐传递给每一个它遇到的小伙伴,森林里充满了温馨和欢乐的气氛。',
-  en: 'In a forest full of autumn vibes, there lived a cheerful turkey named Little Fireball because its feathers were as red as autumn maple leaves. Thanksgiving was approaching, and Little Fireball decided to send blessings to its friends in the forest. It put on a beautiful hat and carefully made a sign that read "Happy Thanksgiving", then set off cheerfully. Along the way, it met many pumpkin friends, all of them wearing new orange clothes as if cheering for the festival. Little Fireball, with full enthusiasm, spread the joy of Thanksgiving to every little friend it met, and the forest was filled with a warm and joyful atmosphere.',
-  es: 'En un bosque repleto de ambiente de otoño, vivía un pavo feliz. Se llamaba Bolita de Fuego, porque sus plumas eran tan rojas como las hojas de arce de otoño. El Día de Acción de Gracias se acercaba, y Bolita de Fuego decidió enviar felicitaciones a los amigos del bosque. Se puso un bonito sombrero, preparó con esmero una placa que decía "Feliz Día de Acción de Gracias" y partió lleno de alegría. En el camino, encontró a muchos amigos calabazas, que también estaban vestidos de ropa nueva anaranjada, como si estuvieran festejando el festival. Bolita de Fuego, con una gran pasión, transmitía la alegría del Día de Acción de Gracias a cada compañero que encontraba, y el bosque estaba lleno de un ambiente cálido y alegre.',
-  pt: 'Em uma floresta cheia de vibrações de outono, vivia um peru alegre chamado Pequena Bola de Fogo, pois suas penas eram tão vermelhas quanto as folhas de bordo de outono. O Dia de Ação de Graças estava se aproximando, e o Pequeno Bola de Fogo decidiu enviar bênçãos aos seus amigos na floresta. Ele colocou um chapéu bonito e cuidadosamente fez um sinal que dizia "Feliz Dia de Ação de Graças", depois saiu alegremente. Ao longo do caminho, ele encontrou muitos amigos abóbora, todos eles vestindo roupas novas laranjas, como se estivessem aplaudindo para o festival. O Pequeno Bola de Fogo, com muita entusiasmo, espalhou a alegria do Dia de Ação de Graças para cada pequeno amigo que encontrou, e a floresta ficou cheia de um clima caloroso e alegre.',
-  ja: '秋の気配が満ちた森に、楽しい七面鳥が住んでいました。その七面鳥は「小さな火の玉」と呼ばれていました。なぜなら、その羽は秋の紅葉のように真っ赤だったからです。感謝祭が近づいてきたので、小さな火の玉は森の友達に祝福を送ることにしました。きれいな帽子をかぶり、「Happy Thanksgiving」と書いた看板を丁寧に作り、元気よく出発しました。道中、たくさんのカボチャの友達に出会いました。彼らもみんなオレンジ色の新しい服を着て、まるで祝祭を歓迎しているかのようでした。小さな火の玉は満ち満ちた熱意を持って、出会ったすべての友達に感謝祭の喜びを伝えました。森は温かく楽しい雰囲気に包まれました。',
+let printableColoringPage = {
+  zh: '可打印涂色页',
+  en: 'Printable Coloring Page',
+  es: 'Página de Colorear Imprimible',
+  pt: 'Página de Colorir Imprimível',
+  ja: '印刷可能な塗り絵ページ',
 }
 }
 
 
 let homePage = {
 let homePage = {
@@ -38,6 +30,14 @@ let tagPage = {
   ja: 'タグページ',
   ja: 'タグページ',
 }
 }
 
 
+let selectByTag = {
+  zh: '按标签筛选填色页',
+  en: 'Select coloring pages by tag',
+  es: 'Seleccionar páginas de colorear por etiqueta',
+  pt: 'Selecionar páginas de colorir por tag',
+  ja: 'タグで塗り絵ページを選択する',
+}
+
 let about = {
 let about = {
   zh: '关于',
   zh: '关于',
   en: 'About',
   en: 'About',
@@ -78,6 +78,14 @@ let designerColumn = {
   ja: 'デザイナーコラム',
   ja: 'デザイナーコラム',
 }
 }
 
 
+let coloringPageDesigner = {
+  zh: '填色页设计师',
+  en: 'Coloring Page Designer',
+  es: 'Diseñador de Páginas de Colorear',
+  pt: 'Designer de Páginas de Colorir',
+  ja: '塗り絵ページデザイナー',
+}
+
 let publishTime = {
 let publishTime = {
   zh: '发布时间',
   zh: '发布时间',
   en: 'Publish Time',
   en: 'Publish Time',
@@ -144,11 +152,11 @@ let cuteCat = {
 }
 }
 
 
 let cuteCatDescription = {
 let cuteCatDescription = {
-  zh: 'Art Number Coloring 是涂画家的乐园,拥有海量的免费的艺术线条底图,等着你来给它们涂上颜色, 现在,让我们从这只可爱的小猫咪入手,开启愉快的填色之旅吧。',
-  en: `Art Number Coloring is a paradise for painters. It has a vast number of free artistic line drawings waiting for you to color them. Now, let's start this delightful coloring journey with this cute little cat.`,
-  es: 'Art Number Coloring es un paraíso para los pintores. Tiene una gran cantidad de dibujos de líneas artísticas gratuitos esperando que los colorees. Ahora, empecemos este delicioso viaje de coloreado con este lindo gatito.',
-  pt: 'Art Number Coloring é um paraíso para os pintores. Tem uma enorme quantidade de desenhos de linhas artísticas gratuitos esperando que você os pinte. Agora, vamos começar esta deliciosa jornada de colorir com este gatinho fofo.',
-  ja: 'Art Number Coloring は画家の楽園です。たくさんの無料のアートラインの下地絵があり、あなたが色を塗るのを待っています。今、この可愛いネコちゃんから楽しい塗り絵の旅を始めましょう。',
+  zh: '快来享受这张超可爱小猫的免费可打印涂色页带来的乐趣吧!它柔软的毛发和明亮的眼睛,看起来十分惹人喜爱。现在是时候开始玩耍啦。拿起你的彩色铅笔,尽情发挥你的创造力。无论是在家放松还是短暂休息,给这张涂色页上色都会给你带来快乐。免费下载,让涂色冒险之旅开始吧!',
+  en: `Embrace the fun with this free printable coloring page of an extremely cute kitten! Its soft fur and bright eyes make it look so endearing. Now it's time to start play. Grab your colored pencils and let your creativity flow. Whether you're relaxing at home or having a break, coloring this page will bring you joy. Download it for free and let the coloring adventure begin!`,
+  es: 'Disfruta de la diversión con esta página de colorear imprimible gratuita de un gatito extremadamente lindo. Su pelaje suave y sus ojos brillantes lo hacen ver muy adorable. Ahora es el momento de comenzar a jugar. Agarra tus lápices de colores y deja fluir tu creatividad. Ya sea que estés relajándote en casa o tomando un descanso, colorear esta página te traerá alegría. ¡Descárgalo gratis y comienza la aventura de coloreado!',
+  pt: 'Aproveite a diversão com esta página de colorir imprimível gratuita de um gatinho extremamente fofo. Seu pêlo macio e seus olhos brilhantes o tornam muito carinhoso. Agora é hora de começar a brincar. Pegue seus lápis de cor e deixe sua criatividade fluir. Seja você relaxando em casa ou tomando um tempo livre, colorir esta página lhe trará alegria. Baixe - a gratuitamente e comece a aventura de colorir!',
+  ja: 'このとても可愛い子猫の無料で印刷できる塗り絵ページを楽しもう!柔らかな毛並みと明るい目がとても愛らしいです。今こそ遊び始めるときです。色鉛筆を取り、あなたのクリエイティブを存分に発揮してください。自宅でくつろいでいるときでも、休憩を取っているときでも、このページを塗ることで喜びを得られます。無料でダウンロードして、塗り絵の冒険を始めましょう!',
 }
 }
 
 
 let daily = {
 let daily = {
@@ -175,6 +183,14 @@ let allAlbums = {
   ja: 'すべてのアルバム',
   ja: 'すべてのアルバム',
 }
 }
 
 
+let coloringPageAlbum = {
+  zh: '填色页专辑',
+  en: 'Coloring Page Album',
+  es: 'Album de Páginas de Colorear',
+  pt: 'Album de Páginas de Colorir',
+  ja: '塗り絵ページアルバム',
+}
+
 let cuteKids = {
 let cuteKids = {
   zh: '天真孩童',
   zh: '天真孩童',
   en: 'Naive kids',
   en: 'Naive kids',
@@ -217,10 +233,10 @@ let hot = {
 
 
 let special = {
 let special = {
   zh: '彩绘专区',
   zh: '彩绘专区',
-  en: 'Colored Painting',
-  es: 'Pintura Coloreada',
-  pt: 'Pintura colorida',
-  ja: 'カラー絵画',
+  en: 'Special Coloring Pages',
+  es: 'Páginas de Colorear Especiales',
+  pt: 'Páginas de Colorir Especiais',
+  ja: '特殊な塗り絵ページ',
 }
 }
 
 
 let selectAlbums = {
 let selectAlbums = {
@@ -403,17 +419,18 @@ let worksCount = {
 
 
 
 
 let translate = {
 let translate = {
+  printableColoringPage,
   homePage,
   homePage,
   categoryPage,
   categoryPage,
   tagPage,
   tagPage,
+  selectByTag,
   about,
   about,
   feedback,
   feedback,
   contactUs,
   contactUs,
   designer,
   designer,
   designerColumn,
   designerColumn,
+  coloringPageDesigner,
   publishTime,
   publishTime,
-  descTest,
-  titleTest,
   app,
   app,
   appDownload,
   appDownload,
   my,
   my,
@@ -425,6 +442,7 @@ let translate = {
   daily,
   daily,
   album,
   album,
   allAlbums,
   allAlbums,
+  coloringPageAlbum,
   cuteKids,
   cuteKids,
   animalsHappyTime,
   animalsHappyTime,
   more,
   more,

+ 21 - 0
package-lock.json

@@ -14,6 +14,7 @@
         "bluebird": "^3.7.2",
         "bluebird": "^3.7.2",
         "body-parser": "^1.20.3",
         "body-parser": "^1.20.3",
         "connect-redis": "^3.3.3",
         "connect-redis": "^3.3.3",
+        "cookie-parser": "^1.4.7",
         "date-fns": "^4.1.0",
         "date-fns": "^4.1.0",
         "ejs": "^3.1.10",
         "ejs": "^3.1.10",
         "express": "^4.21.2",
         "express": "^4.21.2",
@@ -648,6 +649,26 @@
         "node": ">= 0.6"
         "node": ">= 0.6"
       }
       }
     },
     },
+    "node_modules/cookie-parser": {
+      "version": "1.4.7",
+      "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
+      "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
+      "dependencies": {
+        "cookie": "0.7.2",
+        "cookie-signature": "1.0.6"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/cookie-parser/node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/cookie-signature": {
     "node_modules/cookie-signature": {
       "version": "1.0.6",
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
       "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "bluebird": "^3.7.2",
     "bluebird": "^3.7.2",
     "body-parser": "^1.20.3",
     "body-parser": "^1.20.3",
     "connect-redis": "^3.3.3",
     "connect-redis": "^3.3.3",
+    "cookie-parser": "^1.4.7",
     "date-fns": "^4.1.0",
     "date-fns": "^4.1.0",
     "ejs": "^3.1.10",
     "ejs": "^3.1.10",
     "express": "^4.21.2",
     "express": "^4.21.2",

+ 125 - 30
routes/index.js

@@ -18,16 +18,42 @@ const CACHE_EXPIRES = 60; // 60s刷新一次
 const artSelect = 'name title desc width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId';
 const artSelect = 'name title desc width height date publishTime tags lastMod mystery hasSpecial useSpecialThumb publishVersion lock pageId';
 
 
 
 
+// 路由:设置语言
+router.post('/set-lang', (req, res) => {
+  const lang = req.body.lang;
+  const uri = req.body.uri;
+
+  // 设置cookie来保存用户选择的语言
+  res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+
+  // 重定向会指定的uri页,但是语言要变过来
+  let newUri = `/${lang}${uri.substring(3)}`;
+  res.redirect(newUri);
+});
+
+
+// 首页不带语言路由,确定lang,然后重定向到相关语言版本
 router.get('/', (req, res, next) => {
 router.get('/', (req, res, next) => {
   let locale = utils.lang.getLocale(req.acceptsLanguages());
   let locale = utils.lang.getLocale(req.acceptsLanguages());
   let lang = utils.lang.ensureLanguage(locale);
   let lang = utils.lang.ensureLanguage(locale);
+  // 如果cookies中带有lang,就用cookies的,cookies没有则设置cookies
+  if (req.cookies.lang) {
+    lang = utils.lang.ensureLanguage(req.cookies.lang);
+  }
+  if (!req.cookies.lang || req.cookies.lang != lang) {
+    res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+  }
   return res.redirect(`/${lang}`);
   return res.redirect(`/${lang}`);
 });
 });
 
 
 
 
+// 首页路由
 router.get('/:lang/', function (req, res, next) {
 router.get('/:lang/', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
 
 
     let baseSort = { publishTime: 'desc' };
     let baseSort = { publishTime: 'desc' };
 
 
@@ -81,6 +107,41 @@ router.get('/:lang/', function (req, res, next) {
       doc.size = doc.contents.length;
       doc.size = doc.contents.length;
     }
     }
 
 
+    // 设计师
+    let designers = await models.Art.aggregate([
+      // 首先,过滤出 status = 9000 的文档
+      { $match: { status: 9000 } },
+
+      // 首先,根据 user 字段进行分组,并计算每个 user 出现的次数
+      { $group: { _id: '$user', count: { $sum: 1 } } },
+
+      // 然后,按照 count 字段进行降序排列
+      { $sort: { count: -1 } },
+
+      // 接着,与 users 集合进行连接,以获取用户的详细信息
+      {
+        $lookup: {
+          from: 'users', // 要连接的集合名称
+          localField: '_id', // 本地字段(即上一步分组后的 _id 字段)
+          foreignField: '_id', // 要连接的集合中的字段
+          as: 'userDetails' // 连接后结果存储在新字段 userDetails 中
+        }
+      },
+
+      // 展开 userDetails 数组,以便将用户信息提升到顶层
+      { $unwind: '$userDetails' },
+
+      // 调整输出格式,只保留需要的字段
+      { $project: { _id: 1, user: '$_id', count: 1, username: '$userDetails.username', name: '$userDetails.name' } },
+
+      // 限制返回的记录数量
+      { $limit: 6 }
+    ]);
+
+    for (let doc of designers) {
+      doc.avatar = `/thumbs/v1/avatar/320/${doc._id}.jpeg`;
+    }
+
 
 
     let data = {
     let data = {
       title: meta.homePageTile[lang],
       title: meta.homePageTile[lang],
@@ -89,6 +150,7 @@ router.get('/:lang/', function (req, res, next) {
       recommend,
       recommend,
       special,
       special,
       albums,
       albums,
+      designers,
       translate,
       translate,
       categories,
       categories,
       languages,
       languages,
@@ -102,10 +164,14 @@ router.get('/:lang/', function (req, res, next) {
 });
 });
 
 
 
 
-
+// 分类页路由
 router.get('/:lang/category/:tag?', function (req, res, next) {
 router.get('/:lang/category/:tag?', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let tag = req.params.tag;
     let tag = req.params.tag;
     if (!tag) tag = 'latest';
     if (!tag) tag = 'latest';
 
 
@@ -143,9 +209,14 @@ router.get('/:lang/category/:tag?', function (req, res, next) {
 
 
 });
 });
 
 
+// 标签页路由
 router.get('/:lang/tag/:tag?', function (req, res, next) {
 router.get('/:lang/tag/:tag?', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let tag = req.params.tag;
     let tag = req.params.tag;
     if (!tag) tag = 'latest';
     if (!tag) tag = 'latest';
 
 
@@ -186,9 +257,14 @@ router.get('/:lang/tag/:tag?', function (req, res, next) {
 });
 });
 
 
 
 
+// 搜索页路由
 router.get('/:lang/search', function (req, res, next) {
 router.get('/:lang/search', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let search = req.query.search;
     let search = req.query.search;
 
 
 
 
@@ -226,10 +302,13 @@ router.get('/:lang/search', function (req, res, next) {
 });
 });
 
 
 
 
-
+// special页路由
 router.get('/:lang/special', function (req, res, next) {
 router.get('/:lang/special', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
 
 
     let query = {
     let query = {
       title: meta.specialTitle[lang],
       title: meta.specialTitle[lang],
@@ -266,10 +345,13 @@ router.get('/:lang/special', function (req, res, next) {
 
 
 });
 });
 
 
+// 专辑页路由
 router.get('/:lang/albums', function (req, res, next) {
 router.get('/:lang/albums', function (req, res, next) {
   (async function () {
   (async function () {
-
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
 
 
     // 专辑
     // 专辑
     let albums = await models.ArtAlbum
     let albums = await models.ArtAlbum
@@ -307,11 +389,14 @@ router.get('/:lang/albums', function (req, res, next) {
 
 
 });
 });
 
 
-
+// 专辑详情页路由
 router.get('/:lang/album/:id', function (req, res, next) {
 router.get('/:lang/album/:id', function (req, res, next) {
   (async function () {
   (async function () {
-
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let id = req.params.id;
     let id = req.params.id;
     utils.validators.validateId(id);
     utils.validators.validateId(id);
 
 
@@ -337,8 +422,8 @@ router.get('/:lang/album/:id', function (req, res, next) {
 
 
 
 
     let data = {
     let data = {
-      title: `${doc.title}`,
-      description: `${doc.slogon}`,
+      title: `${doc.title} | ${translate.coloringPageAlbum[lang]}`,
+      description: `${doc.slogon} | ${translate.coloringPageAlbum[lang]}`,
       data: doc,
       data: doc,
       translate,
       translate,
       languages,
       languages,
@@ -352,12 +437,13 @@ router.get('/:lang/album/:id', function (req, res, next) {
 });
 });
 
 
 
 
-
+// 设计师专栏路由
 router.get('/:lang/designers', function (req, res, next) {
 router.get('/:lang/designers', function (req, res, next) {
   (async function () {
   (async function () {
-
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
-
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
 
 
     let docs = await models.Art.aggregate([
     let docs = await models.Art.aggregate([
       // 首先,过滤出 status = 9000 的文档
       // 首先,过滤出 status = 9000 的文档
@@ -396,8 +482,8 @@ router.get('/:lang/designers', function (req, res, next) {
     }
     }
 
 
     let data = {
     let data = {
-      title: meta.designerTitle[lang],
-      description: meta.designerDescription[lang],
+      title: meta.designersTitle[lang],
+      description: meta.designersDescription[lang],
       data: docs,
       data: docs,
       length: docs.length,
       length: docs.length,
       translate,
       translate,
@@ -413,11 +499,14 @@ router.get('/:lang/designers', function (req, res, next) {
 });
 });
 
 
 
 
-
+// 设计师详情页路由
 router.get('/:lang/designer/:id', function (req, res, next) {
 router.get('/:lang/designer/:id', function (req, res, next) {
   (async function () {
   (async function () {
-
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let id = req.params.id;
     let id = req.params.id;
     utils.validators.validateId(id);
     utils.validators.validateId(id);
 
 
@@ -445,7 +534,7 @@ router.get('/:lang/designer/:id', function (req, res, next) {
 
 
 
 
     let data = {
     let data = {
-      title: meta.designerTitle[lang],
+      title: `${meta.designerTitle[lang]}: ${user.username}`,
       description: meta.designerDescription[lang],
       description: meta.designerDescription[lang],
       user,
       user,
       data: result.data,
       data: result.data,
@@ -478,9 +567,14 @@ function getRealId(str) {
   return id;
   return id;
 }
 }
 
 
+// 详情页路由(seo url)
 router.get('/:lang/coloring-page/:str', function (req, res, next) {
 router.get('/:lang/coloring-page/:str', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let str = req.params.str;  // 拟人化的id,形如 beautiful-house-daldkaghlda3232, 最后一个-后面的才是真正的id
     let str = req.params.str;  // 拟人化的id,形如 beautiful-house-daldkaghlda3232, 最后一个-后面的才是真正的id
     let id = getRealId(str);
     let id = getRealId(str);
 
 
@@ -518,8 +612,9 @@ router.get('/:lang/coloring-page/:str', function (req, res, next) {
 
 
     let data = {
     let data = {
       // title: `${doc.name.replace(/[_]+/g, '-')}`,
       // title: `${doc.name.replace(/[_]+/g, '-')}`,
-      title: `${doc.title}`,
+      title: `${doc.title} ${translate.printableColoringPage[lang]}`,
       description: `${doc.desc}`,
       description: `${doc.desc}`,
+      // description: `${meta.detailDescription[lang]}: ${doc.desc}`,
       detail: doc,
       detail: doc,
       data: result.data,
       data: result.data,
       page: result.page,
       page: result.page,
@@ -538,10 +633,14 @@ router.get('/:lang/coloring-page/:str', function (req, res, next) {
 
 
 });
 });
 
 
-
+// 详情页路由(简单版不带seo)
 router.get('/:lang/detail/:id', function (req, res, next) {
 router.get('/:lang/detail/:id', function (req, res, next) {
   (async function () {
   (async function () {
     let lang = utils.lang.ensureLanguage(req.params.lang);
     let lang = utils.lang.ensureLanguage(req.params.lang);
+    if (!req.cookies.lang || req.cookies.lang != lang) {
+      res.cookie('lang', lang, { maxAge: 900000, httpOnly: true });
+    }
+
     let id = req.params.id;
     let id = req.params.id;
 
 
     utils.validators.validateId(id);
     utils.validators.validateId(id);
@@ -576,8 +675,9 @@ router.get('/:lang/detail/:id', function (req, res, next) {
 
 
     let data = {
     let data = {
       // title: `${doc.name.replace(/[_]+/g, '-')}`,
       // title: `${doc.name.replace(/[_]+/g, '-')}`,
-      title: `${doc.title}`,
+      title: `${doc.title} ${translate.printableColoringPage[lang]}`,
       description: `${doc.desc}`,
       description: `${doc.desc}`,
+      // description: `${meta.detailDescription[lang]}: ${doc.desc}`,
       data: doc,
       data: doc,
       translate,
       translate,
       lang,
       lang,
@@ -592,6 +692,7 @@ router.get('/:lang/detail/:id', function (req, res, next) {
 });
 });
 
 
 
 
+// play页路由
 router.get('/play/:id', function (req, res, next) {
 router.get('/play/:id', function (req, res, next) {
   (async function () {
   (async function () {
     let id = req.params.id;
     let id = req.params.id;
@@ -653,16 +754,10 @@ const organizeData = (data, lang) => {
 
 
     doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip`;
     doc.zip = `${host}/zips/v2/number_mini/${version}/${doc._id}.zip`;
 
 
-    let utf8name = encodeURIComponent(doc.name.replace(/[\s_]+/g, '-')).toLowerCase();
-    if (doc.title) { // 如果有英文title,就用英文title作为url,而不用原来的name字段,name字段没有多语
+    if (doc.title) {
       try {
       try {
         let titleJson = JSON.parse(doc.title);
         let titleJson = JSON.parse(doc.title);
-        if (titleJson && titleJson.en) {
-          utf8name = encodeURIComponent(titleJson.en.replace(/[\s_]+/g, '-')).toLowerCase();
-        }
-        if (titleJson && titleJson[lang]) {
-          doc.title = titleJson[lang];
-        }
+        doc.title = titleJson && titleJson[lang] ? titleJson[lang] : doc.name;
       } catch (e) {
       } catch (e) {
         console.error(e.message);
         console.error(e.message);
       }
       }
@@ -670,8 +765,8 @@ const organizeData = (data, lang) => {
       doc.title = doc.name;
       doc.title = doc.name;
     }
     }
 
 
-
-    doc.uri = `${lang}/coloring-page/${utf8name}-${doc._id}`;
+    let utf8name = encodeURIComponent(doc.title.replace(/[\s_]+/g, '-')).toLowerCase();
+    doc.uri = `/${lang}/coloring-page/${utf8name}-${doc._id}`;
 
 
     delete doc.hasSpecial;
     delete doc.hasSpecial;
     delete doc.useSpecialThumb;
     delete doc.useSpecialThumb;
@@ -726,8 +821,8 @@ const organizeDetail = (doc, lang) => {
   }
   }
   doc.publishTime = format(new Date(doc.publishTime), 'yyyy/MM/dd');
   doc.publishTime = format(new Date(doc.publishTime), 'yyyy/MM/dd');
 
 
-  let utf8name = encodeURIComponent(doc.name.replace(/[\s_]+/g, '-'));
-  doc.uri = `${utf8name}-${doc._id}`;
+  let utf8name = encodeURIComponent(doc.title.replace(/[\s_]+/g, '-')).toLowerCase();
+  doc.uri = `/${lang}/coloring-page/${utf8name}-${doc._id}`;
 
 
   delete doc.hasSpecial;
   delete doc.hasSpecial;
   delete doc.useSpecialThumb;
   delete doc.useSpecialThumb;

+ 67 - 0
test/language.html

@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Custom Language Selector</title>
+  <style>
+    /* 简单的样式使下拉菜单看起来更好 */
+    .language-selector {
+      display: inline-block;
+      position: relative;
+    }
+
+    .language-selector select {
+      padding: 5px 10px;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      appearance: none;
+      -webkit-appearance: none;
+      /* Safari and Chrome */
+      -moz-appearance: none;
+      /* Firefox */
+    }
+
+    .language-selector::after {
+      content: '\25BC';
+      /* 下拉箭头 */
+      position: absolute;
+      top: 50%;
+      right: 10px;
+      transform: translateY(-50%);
+      pointer-events: none;
+    }
+  </style>
+</head>
+
+<body>
+  <h1>Select Your Language</h1>
+  <div class="language-selector">
+    <form id="languageForm" action="/set-language" method="post">
+      <select name="language_code" onchange="submitLanguageForm()">
+        <option value="en" selected>English</option>
+        <option value="zh-CN">中文 (简体)</option>
+        <option value="fr">Français</option>
+        <option value="es">Español</option>
+        <!-- 添加更多语言选项 -->
+      </select>
+      <!-- 隐藏的输入字段用于存储当前URI,可以通过JavaScript动态设置 -->
+      <input type="hidden" name="current_uri" id="currentUri" value="">
+    </form>
+  </div>
+
+  <script>
+    // 页面加载时设置当前URI
+    window.onload = function () {
+      document.getElementById('currentUri').value = window.location.href;
+    };
+
+    // 当用户选择语言时提交表单
+    function submitLanguageForm() {
+      document.getElementById('languageForm').submit();
+    }
+  </script>
+</body>
+
+</html>

+ 3 - 2
views/album-section.ejs

@@ -9,8 +9,9 @@
     <div class="album-icon-grid">
     <div class="album-icon-grid">
         <% albums.forEach(album => { %>
         <% albums.forEach(album => { %>
           <div class="album-grid-card">
           <div class="album-grid-card">
-            <a href="/<%= lang %>/album/<%= album._id %>"><img src="<%= album.icon %>" alt="<%= album.title %>" ></a>
-            <div style="padding: 0px 0px 4px 4px"><%= album.title %></div>
+            <div style="padding: 2px; font-size: 14px; color: grey;"><%= translate.coloringPageAlbum[lang] %></div>
+            <a href="/<%= lang %>/album/<%= album._id %>"><img src="<%= album.icon %>" alt="<%= translate.coloringPageAlbum[lang] %>: <%= album.title %>" ></a>
+            <div style="padding: 2px; font-weight: bold;"><%= album.title %></div>
           </div>
           </div>
         <% }); %>
         <% }); %>
     </div>
     </div>

+ 6 - 4
views/album.ejs

@@ -3,8 +3,8 @@
 
 
 <head>
 <head>
   <%- include('common-meta') %>
   <%- include('common-meta') %>
-  <link rel="stylesheet" href="/stylesheets/styles.css">
-  <link rel="stylesheet" href="/stylesheets/album.css">
+    <link rel="stylesheet" href="/stylesheets/styles.css">
+    <link rel="stylesheet" href="/stylesheets/album.css">
 </head>
 </head>
 
 
 <body>
 <body>
@@ -14,7 +14,8 @@
       <h1 style="color: purple">
       <h1 style="color: purple">
         <%= data.title %>
         <%= data.title %>
       </h1>
       </h1>
-      <img style="border-radius: 8px;" src="<%= data.cover %>" alt="<%= data.title %>">
+      <img style="border-radius: 8px;" src="<%= data.cover %>"
+        alt="<%= data.title %> | <%= translate.coloringPageAlbum[lang] %>">
       <div style="color: gray; font-size: 18px; padding: 10px">
       <div style="color: gray; font-size: 18px; padding: 10px">
         <%= data.slogon %>
         <%= data.slogon %>
       </div>
       </div>
@@ -23,7 +24,8 @@
     <div class="content">
     <div class="content">
       <div class="image-grid">
       <div class="image-grid">
         <% data.contents.forEach(item=> { %>
         <% data.contents.forEach(item=> { %>
-          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
+          <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+              alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
           <% }); %>
           <% }); %>
       </div>
       </div>
     </div>
     </div>

+ 1 - 1
views/albums.ejs

@@ -25,7 +25,7 @@
       <div class="album-grid">
       <div class="album-grid">
         <% data.forEach(album=> { %>
         <% data.forEach(album=> { %>
           <div class="album-grid-card">
           <div class="album-grid-card">
-            <a href="/<%= lang %>/album/<%= album._id %>"><img src="<%= album.cover %>" alt="<%= album.title %>"></a>
+            <a href="/<%= lang %>/album/<%= album._id %>"><img src="<%= album.cover %>" alt="<%= album.title %> | <%= translate.coloringPageAlbum[lang] %>"></a>
             <div style="padding: 0px 0px 4px 4px">
             <div style="padding: 0px 0px 4px 4px">
               <%= album.title %>
               <%= album.title %>
             </div>
             </div>

+ 6 - 6
views/banner.ejs

@@ -4,7 +4,7 @@
   <button class="btn-prev">❮</button>
   <button class="btn-prev">❮</button>
   <div class="carousel-images">
   <div class="carousel-images">
     <div class="item" style="display: flex; flex-direction: row; justify-content: center; align-items: center;">
     <div class="item" style="display: flex; flex-direction: row; justify-content: center; align-items: center;">
-      <img width="50%" src="http://color.jccytech.cn/thumbs/v2/page/480/5d8898684278e31afd9b6968.jpeg" alt="<%= translate.cuteCat[lang] %>">
+      <img width="50%" src="http://color.jccytech.cn/thumbs/v2/page/480/5d8898684278e31afd9b6968.jpeg" alt="<%= translate.cuteCat[lang] %> <%= translate.printableColoringPage[lang]%>">
       <div style="padding-right: 60px;">
       <div style="padding-right: 60px;">
         <h1><%= translate.cuteCat[lang] %></h1>
         <h1><%= translate.cuteCat[lang] %></h1>
         <p class="cat-description"><%= translate.cuteCatDescription[lang] %></p>
         <p class="cat-description"><%= translate.cuteCatDescription[lang] %></p>
@@ -18,18 +18,18 @@
         <div style="font-weight: bold; font-size: 20px; color: #aaa; padding-bottom: 20px;">2025/01/19</div>
         <div style="font-weight: bold; font-size: 20px; color: #aaa; padding-bottom: 20px;">2025/01/19</div>
         <a href="/play/6783978993b021143d362277" class="carousel-play-btn"><%= translate.play[lang] %></a>
         <a href="/play/6783978993b021143d362277" class="carousel-play-btn"><%= translate.play[lang] %></a>
       </div>
       </div>
-      <img width="40%" src="http://color.jccytech.cn/thumbs/v2/page/480/6783978993b021143d362277.jpeg" alt="<%= translate.daily[lang] %>">
+      <img width="40%" src="http://color.jccytech.cn/thumbs/v2/page/480/6783978993b021143d362277.jpeg" alt="<%= translate.daily[lang] %> <%= translate.printableColoringPage[lang]%>"">
     </div>
     </div>
 
 
     <div class="item" style="position: relative; display: inline-block;">
     <div class="item" style="position: relative; display: inline-block;">
-      <img width="100%" src="https://color.jccytech.cn/res/coloring/album_cover/720/661cf6deaae27d6dda120bcc.jpeg" alt="<%= translate.cuteKids[lang] %>">
-      <h3 style="position: absolute; top: 20px; left: 20px; color:#ccc; "><%= translate.album[lang] %></h3>
+      <img width="100%" src="https://color.jccytech.cn/res/coloring/album_cover/720/661cf6deaae27d6dda120bcc.jpeg" alt="<%= translate.coloringPageAlbum[lang] %>: <%= translate.cuteKids[lang] %>">
+      <h3 style="position: absolute; top: 20px; left: 20px; color:#ccc; "><%= translate.coloringPageAlbum[lang] %></h3>
       <h1 style="position: absolute; top: 40px; left: 20px; color:white; "><%= translate.cuteKids[lang] %></h1>
       <h1 style="position: absolute; top: 40px; left: 20px; color:white; "><%= translate.cuteKids[lang] %></h1>
     </div>
     </div>
 
 
     <div class="item" style="position: relative; display: inline-block;">
     <div class="item" style="position: relative; display: inline-block;">
-      <img width="100%" src="https://color.jccytech.cn/res/coloring/album_cover/720/670e26713562c348e1b64db0.jpeg" alt="<%= translate.animalsHappyTime[lang] %>">
-      <h3 style="position: absolute; top: 20px; left: 20px; color:#ccc; "><%= translate.album[lang] %></h3>
+      <img width="100%" src="https://color.jccytech.cn/res/coloring/album_cover/720/670e26713562c348e1b64db0.jpeg" alt="<%= translate.coloringPageAlbum[lang] %>: <%= translate.animalsHappyTime[lang] %>">
+      <h3 style="position: absolute; top: 20px; left: 20px; color:#ccc; "><%= translate.coloringPageAlbum[lang] %></h3>
       <h1 style="position: absolute; top: 40px; left: 20px; color:white; "><%= translate.animalsHappyTime[lang] %></h1>
       <h1 style="position: absolute; top: 40px; left: 20px; color:white; "><%= translate.animalsHappyTime[lang] %></h1>
     </div>
     </div>
 
 

+ 10 - 9
views/category.ejs

@@ -4,14 +4,14 @@
 <head>
 <head>
   <%- include('common-meta') %>
   <%- include('common-meta') %>
 
 
-  <link rel="alternate" href="https://art.pcoloring.com/en/category" hrefLang="en" />
-  <link rel="alternate" href="https://art.pcoloring.com/zh/category" hrefLang="zh" />
-  <link rel="alternate" href="https://art.pcoloring.com/es/category" hrefLang="es" />
-  <link rel="alternate" href="https://art.pcoloring.com/pt/category" hrefLang="pt" />
-  <link rel="alternate" href="https://art.pcoloring.com/ja/category" hrefLang="ja" />
-
-  <link rel="stylesheet" href="/stylesheets/styles.css">
-  <link rel="stylesheet" href="/stylesheets/category.css">
+    <link rel="alternate" href="https://art.pcoloring.com/en/category" hrefLang="en" />
+    <link rel="alternate" href="https://art.pcoloring.com/zh/category" hrefLang="zh" />
+    <link rel="alternate" href="https://art.pcoloring.com/es/category" hrefLang="es" />
+    <link rel="alternate" href="https://art.pcoloring.com/pt/category" hrefLang="pt" />
+    <link rel="alternate" href="https://art.pcoloring.com/ja/category" hrefLang="ja" />
+
+    <link rel="stylesheet" href="/stylesheets/styles.css">
+    <link rel="stylesheet" href="/stylesheets/category.css">
 </head>
 </head>
 
 
 <body>
 <body>
@@ -30,7 +30,8 @@
     <div class="content">
     <div class="content">
       <div class="image-grid">
       <div class="image-grid">
         <% data.forEach(item=> { %>
         <% data.forEach(item=> { %>
-          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
+          <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+              alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
           <% }); %>
           <% }); %>
       </div>
       </div>
     </div>
     </div>

+ 22 - 0
views/designer-section.ejs

@@ -0,0 +1,22 @@
+
+<link rel="stylesheet" href="/stylesheets/designer.css">
+
+<div class="content-wrapper">
+  <div class="content-title">
+    <div style="font-size: 20px; font-weight: bold;"><%= translate.designerColumn[lang] %>:</div>
+    <a href="<%= lang %>/designers"><%= translate.more[lang] %> >>></a>
+  </div>
+
+  <div class="container">
+    <% designers.forEach(item=> { %>
+      <div class="card">
+        <a href="/<%= lang %>/designer/<%= item._id %>"><img src="<%= item.avatar %>" alt="<%= item.username %> | <%= translate.coloringPageDesigner[lang]%>"></a>
+        <div class="info">
+          <p><strong><%= item.username %></strong></p>
+          <p><%= translate.worksCount[lang] %>: <strong><%= item.count %></strong></p>
+        </div>
+      </div>
+      <% }); %>
+  </div>
+
+</div>

+ 15 - 7
views/designer.ejs

@@ -3,8 +3,8 @@
 
 
 <head>
 <head>
   <%- include('common-meta') %>
   <%- include('common-meta') %>
-  <link rel="stylesheet" href="/stylesheets/styles.css">
-  <link rel="stylesheet" href="/stylesheets/designer.css">
+    <link rel="stylesheet" href="/stylesheets/styles.css">
+    <link rel="stylesheet" href="/stylesheets/designer.css">
 </head>
 </head>
 
 
 <body>
 <body>
@@ -12,10 +12,17 @@
 
 
     <div style="display: flex; justify-content: center; align-items: center; margin-top: 20px;">
     <div style="display: flex; justify-content: center; align-items: center; margin-top: 20px;">
       <div class="card" style="width: 200px;">
       <div class="card" style="width: 200px;">
-        <a href="/<%= lang %>/designer/<%= user._id %>"><img src="<%= user.avatar %>" alt="<%= user.username %>"></a>
+        <a href="/<%= lang %>/designer/<%= user._id %>"><img src="<%= user.avatar %>"
+            alt="<%= user.username %> | <%= translate.coloringPageDesigner[lang]%>"></a>
         <div class="info">
         <div class="info">
-          <p><strong><%= user.username %></strong></p>
-          <p><%= translate.worksCount[lang] %>: <strong><%= user.count %></strong></p>
+          <p><strong>
+              <%= user.username %>
+            </strong></p>
+          <p>
+            <%= translate.worksCount[lang] %>: <strong>
+                <%= user.count %>
+              </strong>
+          </p>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
@@ -23,14 +30,15 @@
     <div class="content">
     <div class="content">
       <div class="image-grid">
       <div class="image-grid">
         <% data.forEach(item=> { %>
         <% data.forEach(item=> { %>
-          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
+          <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+              alt="<%= item.title %> | <%= translate.printableColoringPage[lang] %>"></a>
           <% }); %>
           <% }); %>
       </div>
       </div>
     </div>
     </div>
 
 
     <div style="padding: 40px"></div>
     <div style="padding: 40px"></div>
 
 
-  <%- include('pagination') %>
+    <%- include('pagination') %>
 
 
 </body>
 </body>
 
 

+ 1 - 1
views/designers.ejs

@@ -24,7 +24,7 @@
     <div class="container">
     <div class="container">
       <% data.forEach(item=> { %>
       <% data.forEach(item=> { %>
         <div class="card">
         <div class="card">
-          <a href="/<%= lang %>/designer/<%= item._id %>"><img src="<%= item.avatar %>" alt="<%= item.username %>"></a>
+          <a href="/<%= lang %>/designer/<%= item._id %>"><img src="<%= item.avatar %>" alt="<%= item.username %> | <%= translate.coloringPageDesigner[lang]%>"></a>
           <div class="info">
           <div class="info">
             <p><strong><%= item.username %></strong></p>
             <p><strong><%= item.username %></strong></p>
             <p><%= translate.worksCount[lang] %>: <strong><%= item.count %></strong></p>
             <p><%= translate.worksCount[lang] %>: <strong><%= item.count %></strong></p>

+ 51 - 33
views/detail.ejs

@@ -2,48 +2,66 @@
 <html lang="<%= lang %>">
 <html lang="<%= lang %>">
 
 
 <head>
 <head>
-  <%- include('common-meta') %>
-  
-  <link rel="alternate" href="https://art.pcoloring.com/en/<%= uri.substring(3) %>" hrefLang="en" />
-  <link rel="alternate" href="https://art.pcoloring.com/zh/<%= uri.substring(3) %>" hrefLang="zh" />
-  <link rel="alternate" href="https://art.pcoloring.com/es/<%= uri.substring(3) %>" hrefLang="es" />
-  <link rel="alternate" href="https://art.pcoloring.com/pt/<%= uri.substring(3) %>" hrefLang="pt" />
-  <link rel="alternate" href="https://art.pcoloring.com/ja/<%= uri.substring(3) %>" hrefLang="ja" />
-
-  <link rel="stylesheet" href="/stylesheets/styles.css">
-  <link rel="stylesheet" href="/stylesheets/detail.css">
+    <%- include('common-meta') %>
+
+        <link rel="alternate" href="https://art.pcoloring.com/en/<%= uri.substring(3) %>" hrefLang="en" />
+        <link rel="alternate" href="https://art.pcoloring.com/zh/<%= uri.substring(3) %>" hrefLang="zh" />
+        <link rel="alternate" href="https://art.pcoloring.com/es/<%= uri.substring(3) %>" hrefLang="es" />
+        <link rel="alternate" href="https://art.pcoloring.com/pt/<%= uri.substring(3) %>" hrefLang="pt" />
+        <link rel="alternate" href="https://art.pcoloring.com/ja/<%= uri.substring(3) %>" hrefLang="ja" />
+
+        <link rel="stylesheet" href="/stylesheets/styles.css">
+        <link rel="stylesheet" href="/stylesheets/detail.css">
 </head>
 </head>
 
 
 <body>
 <body>
     <%- include('header') %>
     <%- include('header') %>
-    <div class="details">
-        <div class="poster"><img src="<%= detail.thumb %>" alt="<%= detail.title %>"></div>
-        <div class="description">
-            <div style="font-size: 30px; font-weight: 700;"><%= detail.title %></div>
-            <p><%= translate.designer[lang] %>: <a href="/<%= lang %>/designer/<%= detail.user._id %>" class="tag-button"><%= detail.user.username %></a></p>
-            <p><%= translate.publishTime[lang] %>: <%= detail.publishTime %></p>
-            <p>
-            <%= translate.tag[lang] %>:
-            <% detail.tags.forEach(tag=> { %>
-                <a href="/<%= lang %>/tag/<%= tag %>" class="tag-button"><%= tag %></a>
-            <% }); %>
-            </p>
-            <div><%= detail.desc %></div>
-            <a href="/play/<%= detail._id %>" class="play-button"><%= translate.play[lang] %></a>
+        <div class="details">
+            <div class="poster"><img src="<%= detail.thumb %>" alt="<%= detail.title %>"></div>
+            <div class="description">
+                <div style="font-size: 30px; font-weight: 700;">
+                    <%= detail.title %>
+                </div>
+                <p>
+                    <%= translate.designer[lang] %>: <a href="/<%= lang %>/designer/<%= detail.user._id %>"
+                            class="tag-button">
+                            <%= detail.user.username %>
+                        </a>
+                </p>
+                <p>
+                    <%= translate.publishTime[lang] %>: <%= detail.publishTime %>
+                </p>
+                <p>
+                    <%= translate.tag[lang] %>:
+                        <% detail.tags.forEach(tag=> { %>
+                            <a href="/<%= lang %>/tag/<%= tag %>" class="tag-button">
+                                <%= tag %>
+                            </a>
+                            <% }); %>
+                </p>
+                <div>
+                    <%= detail.desc %>
+                </div>
+                <a href="/play/<%= detail._id %>" class="play-button">
+                    <%= translate.play[lang] %>
+                </a>
+            </div>
         </div>
         </div>
-    </div>
 
 
-    <p style="display: flex; justify-content: center; color: #777; font-size: 18px; font-weight: 500;"><%= translate.mayYouLike[lang] %>:</p>
+        <p style="display: flex; justify-content: center; color: #777; font-size: 18px; font-weight: 500;">
+            <%= translate.mayYouLike[lang] %>:
+        </p>
 
 
-    <div class="content" style="margin-bottom: 40px;">
-        <div class="image-grid">
-            <% relates.forEach(item=> { %>
-            <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
-            <% }); %>
+        <div class="content" style="margin-bottom: 40px;">
+            <div class="image-grid">
+                <% relates.forEach(item=> { %>
+                    <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+                            alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
+                    <% }); %>
+            </div>
         </div>
         </div>
-    </div>
 
 
-    <%- include('pagination') %>
+        <%- include('pagination') %>
 
 
 </body>
 </body>
 
 

+ 54 - 11
views/header.ejs

@@ -12,18 +12,38 @@
         stroke-linejoin="round" />
         stroke-linejoin="round" />
     </svg>
     </svg>
     <div class="dropdown-home-content">
     <div class="dropdown-home-content">
-      <a href="/<%= lang %>" class="<%= uri == `/${lang}` ? 'selected' : '' %>"><%= translate.homePage[lang] %></a>
-      <a href="/<%= lang %>/category" class="<%= uri.includes(`/${lang}/category`) ? 'selected' : '' %>"><%= translate.categoryPage[lang] %></a>
-      <a href="/<%= lang %>/tag" class="<%= uri.includes(`/${lang}/tag`) ? 'selected' : '' %>"><%= translate.tagPage[lang] %></a>
-      <a href="/<%= lang %>/albums" class="<%= uri.includes(`/${lang}/albums`) ? 'selected' : '' %>"><%= translate.album[lang] %></a>
-      <a href="/<%= lang %>/special" class="<%= uri.includes(`/${lang}/special`) ? 'selected' : '' %>"><%= translate.special[lang] %></a>
-      <a href="/<%= lang %>/designers" class="<%= uri.includes(`/${lang}/designers`) ? 'selected' : '' %>"><%= translate.designerColumn[lang] %></a>
+      <a href="/<%= lang %>" class="<%= uri == `/${lang}` ? 'selected' : '' %>">
+        <%= translate.homePage[lang] %>
+      </a>
+      <a href="/<%= lang %>/category" class="<%= uri.includes(`/${lang}/category`) ? 'selected' : '' %>">
+        <%= translate.categoryPage[lang] %>
+      </a>
+      <a href="/<%= lang %>/tag" class="<%= uri.includes(`/${lang}/tag`) ? 'selected' : '' %>">
+        <%= translate.tagPage[lang] %>
+      </a>
+      <a href="/<%= lang %>/albums" class="<%= uri.includes(`/${lang}/albums`) ? 'selected' : '' %>">
+        <%= translate.album[lang] %>
+      </a>
+      <a href="/<%= lang %>/special" class="<%= uri.includes(`/${lang}/special`) ? 'selected' : '' %>">
+        <%= translate.special[lang] %>
+      </a>
+      <a href="/<%= lang %>/designers" class="<%= uri.includes(`/${lang}/designers`) ? 'selected' : '' %>">
+        <%= translate.designerColumn[lang] %>
+      </a>
       <div class="divider"></div>
       <div class="divider"></div>
+      <!--
       <a href="/<%= lang %>/my-works"><%= translate.my[lang] %></a>
       <a href="/<%= lang %>/my-works"><%= translate.my[lang] %></a>
       <div class="divider"></div>
       <div class="divider"></div>
-      <a href="/<%= lang %>/info#app" class="<%= uri.includes(`/${lang}/info#app`) ? 'selected' : '' %>"><%= translate.app[lang] %></a>
-      <a href="/<%= lang %>/info#about" class="<%= uri.includes(`/${lang}/info#about`) ? 'selected' : '' %>"><%= translate.about[lang] %></a>
-      <a href="/<%= lang %>/info#contact" class="<%= uri.includes(`/${lang}/info#contact`) ? 'selected' : '' %>"><%= translate.contactUs[lang] %></a>
+      -->
+      <a href="/<%= lang %>/info#app" class="<%= uri.includes(`/${lang}/info#app`) ? 'selected' : '' %>">
+        <%= translate.app[lang] %>
+      </a>
+      <a href="/<%= lang %>/info#about" class="<%= uri.includes(`/${lang}/info#about`) ? 'selected' : '' %>">
+        <%= translate.about[lang] %>
+      </a>
+      <a href="/<%= lang %>/info#contact" class="<%= uri.includes(`/${lang}/info#contact`) ? 'selected' : '' %>">
+        <%= translate.contactUs[lang] %>
+      </a>
       <div class="divider"></div>
       <div class="divider"></div>
       <p class="copyright">Copyright &copy; 2025 Art Number Coloring All Rights Reserved</p>
       <p class="copyright">Copyright &copy; 2025 Art Number Coloring All Rights Reserved</p>
     </div>
     </div>
@@ -59,13 +79,36 @@
       </svg>
       </svg>
       <div class="dropdown-content">
       <div class="dropdown-content">
         <% languages.forEach(lg=> { %>
         <% languages.forEach(lg=> { %>
-          <a href="/<%= lg.code %>" class="<%= lg.code == lang ? 'selected' : '' %>" style="font-size: 16px"><%= lg.title %></a>
+          <a href="<%=`/${lg.code}${uri.substring(3)}` %>" class="<%= lg.code == lang ? 'selected' : '' %>"
+            style="font-size: 16px">
+            <%= lg.title %>
+          </a>
           <% }); %>
           <% }); %>
       </div>
       </div>
+      <!-- <div class="dropdown-content">
+        <form id="languageForm" action="/set-lang" method="post">
+          <select name="lang" onchange="submitLanguageForm()">
+            <% languages.forEach(lg=> { %>
+              <option value="<%= lg.code %>" <%=lg.code==lang ? 'selected' : '' %> >
+                <%= lg.title %>
+              </option>
+              <% }); %>
+          </select>
+          <input type="hidden" name="uri" value="<%= uri%>">
+        </form>
+      </div> -->
+
     </div>
     </div>
 
 
     <!-- <a href="/<%= lang %>/app" class="header-right-btn"><%= translate.app[lang] %></a>
     <!-- <a href="/<%= lang %>/app" class="header-right-btn"><%= translate.app[lang] %></a>
     <a href="/<%= lang %>/my-works" class="header-right-btn"><%= translate.my[lang] %></a> -->
     <a href="/<%= lang %>/my-works" class="header-right-btn"><%= translate.my[lang] %></a> -->
   </div>
   </div>
 
 
-</header>
+</header>
+
+<script>
+  // 当用户选择语言时提交表单
+  function submitLanguageForm() {
+    document.getElementById('languageForm').submit();
+  }
+</script>

+ 10 - 6
views/hot-section.ejs

@@ -1,16 +1,20 @@
-
 <div class="content-wrapper">
 <div class="content-wrapper">
   <div class="content-title">
   <div class="content-title">
-    <div style="font-size: 20px; font-weight: bold;"><%= translate.hot[lang] %>:</div>
-    <a href="/<%= lang %>/category/data_good"><%= translate.more[lang] %>>>></a>
+    <div style="font-size: 20px; font-weight: bold;">
+      <%= translate.hot[lang] %>:
+    </div>
+    <a href="/<%= lang %>/category/data_good">
+      <%= translate.more[lang] %>>>>
+    </a>
   </div>
   </div>
 
 
   <div class="content">
   <div class="content">
     <div class="image-grid">
     <div class="image-grid">
-        <% recommend.forEach(item => { %>
-            <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>" ></a>
+      <% recommend.forEach(item=> { %>
+        <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+            alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
         <% }); %>
         <% }); %>
     </div>
     </div>
   </div>
   </div>
 
 
-</div>
+</div>

+ 1 - 0
views/index.ejs

@@ -18,6 +18,7 @@
   <%- include('latest-section') %>
   <%- include('latest-section') %>
   <%- include('album-section') %>
   <%- include('album-section') %>
   <%- include('hot-section') %>
   <%- include('hot-section') %>
+  <%- include('designer-section') %>
   <%- include('special-section') %>
   <%- include('special-section') %>
   <%- include('footer') %>
   <%- include('footer') %>
   <div style="height: 50px;"></div>
   <div style="height: 50px;"></div>

+ 2 - 1
views/latest-section.ejs

@@ -11,7 +11,8 @@
   <div class="content">
   <div class="content">
     <div class="image-grid">
     <div class="image-grid">
       <% latest.forEach(item=> { %>
       <% latest.forEach(item=> { %>
-        <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
+        <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+            alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
         <% }); %>
         <% }); %>
     </div>
     </div>
   </div>
   </div>

+ 7 - 7
views/search.ejs

@@ -4,13 +4,13 @@
 <head>
 <head>
   <%- include('common-meta') %>
   <%- include('common-meta') %>
 
 
-  <link rel="alternate" href="https://art.pcoloring.com/en/search" hrefLang="en" />
-  <link rel="alternate" href="https://art.pcoloring.com/zh/search" hrefLang="zh" />
-  <link rel="alternate" href="https://art.pcoloring.com/es/search" hrefLang="es" />
-  <link rel="alternate" href="https://art.pcoloring.com/pt/search" hrefLang="pt" />
-  <link rel="alternate" href="https://art.pcoloring.com/ja/search" hrefLang="ja" />
+    <link rel="alternate" href="https://art.pcoloring.com/en/search" hrefLang="en" />
+    <link rel="alternate" href="https://art.pcoloring.com/zh/search" hrefLang="zh" />
+    <link rel="alternate" href="https://art.pcoloring.com/es/search" hrefLang="es" />
+    <link rel="alternate" href="https://art.pcoloring.com/pt/search" hrefLang="pt" />
+    <link rel="alternate" href="https://art.pcoloring.com/ja/search" hrefLang="ja" />
 
 
-  <link rel="stylesheet" href="/stylesheets/styles.css">
+    <link rel="stylesheet" href="/stylesheets/styles.css">
 
 
 </head>
 </head>
 
 
@@ -20,7 +20,7 @@
     <div class="content">
     <div class="content">
       <div class="image-grid">
       <div class="image-grid">
         <% data.forEach(item=> { %>
         <% data.forEach(item=> { %>
-          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
+          <a href="<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
           <% }); %>
           <% }); %>
       </div>
       </div>
     </div>
     </div>

+ 10 - 6
views/special-section.ejs

@@ -1,16 +1,20 @@
-
 <div class="content-wrapper">
 <div class="content-wrapper">
   <div class="content-title">
   <div class="content-title">
-    <div style="font-size: 20px; font-weight: bold;"><%= translate.special[lang] %>:</div>
-    <a href="/<%= lang %>/special"><%= translate.more[lang] %>>>></a>
+    <div style="font-size: 20px; font-weight: bold;">
+      <%= translate.special[lang] %>:
+    </div>
+    <a href="/<%= lang %>/special">
+      <%= translate.more[lang] %>>>>
+    </a>
   </div>
   </div>
 
 
   <div class="content">
   <div class="content">
     <div class="image-grid">
     <div class="image-grid">
-        <% special.forEach(item => { %>
-            <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>" ></a>
+      <% special.forEach(item=> { %>
+        <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+            alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
         <% }); %>
         <% }); %>
     </div>
     </div>
   </div>
   </div>
 
 
-</div>
+</div>

+ 8 - 7
views/special.ejs

@@ -4,13 +4,13 @@
 <head>
 <head>
   <%- include('common-meta') %>
   <%- include('common-meta') %>
 
 
-  <link rel="alternate" href="https://art.pcoloring.com/en/special" hrefLang="en" />
-  <link rel="alternate" href="https://art.pcoloring.com/zh/special" hrefLang="zh" />
-  <link rel="alternate" href="https://art.pcoloring.com/es/special" hrefLang="es" />
-  <link rel="alternate" href="https://art.pcoloring.com/pt/special" hrefLang="pt" />
-  <link rel="alternate" href="https://art.pcoloring.com/ja/special" hrefLang="ja" />
+    <link rel="alternate" href="https://art.pcoloring.com/en/special" hrefLang="en" />
+    <link rel="alternate" href="https://art.pcoloring.com/zh/special" hrefLang="zh" />
+    <link rel="alternate" href="https://art.pcoloring.com/es/special" hrefLang="es" />
+    <link rel="alternate" href="https://art.pcoloring.com/pt/special" hrefLang="pt" />
+    <link rel="alternate" href="https://art.pcoloring.com/ja/special" hrefLang="ja" />
 
 
-  <link rel="stylesheet" href="/stylesheets/styles.css">
+    <link rel="stylesheet" href="/stylesheets/styles.css">
 
 
 </head>
 </head>
 
 
@@ -22,7 +22,8 @@
     <div class="content">
     <div class="content">
       <div class="image-grid">
       <div class="image-grid">
         <% data.forEach(item=> { %>
         <% data.forEach(item=> { %>
-          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.name %>"></a>
+          <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+              alt="<%= item.title %> | <%= translate.printableColoringPage[lang] %>"></a>
           <% }); %>
           <% }); %>
       </div>
       </div>
     </div>
     </div>

+ 22 - 14
views/tag.ejs

@@ -4,35 +4,43 @@
 <head>
 <head>
   <%- include('common-meta') %>
   <%- include('common-meta') %>
 
 
-  <link rel="alternate" href="https://art.pcoloring.com/en/tag" hrefLang="en" />
-  <link rel="alternate" href="https://art.pcoloring.com/zh/tag" hrefLang="zh" />
-  <link rel="alternate" href="https://art.pcoloring.com/es/tag" hrefLang="es" />
-  <link rel="alternate" href="https://art.pcoloring.com/pt/tag" hrefLang="pt" />
-  <link rel="alternate" href="https://art.pcoloring.com/ja/tag" hrefLang="ja" />
-
-  <link rel="stylesheet" href="/stylesheets/styles.css">
-  <link rel="stylesheet" href="/stylesheets/tag.css">
+    <link rel="alternate" href="https://art.pcoloring.com/en/tag" hrefLang="en" />
+    <link rel="alternate" href="https://art.pcoloring.com/zh/tag" hrefLang="zh" />
+    <link rel="alternate" href="https://art.pcoloring.com/es/tag" hrefLang="es" />
+    <link rel="alternate" href="https://art.pcoloring.com/pt/tag" hrefLang="pt" />
+    <link rel="alternate" href="https://art.pcoloring.com/ja/tag" hrefLang="ja" />
+
+    <link rel="stylesheet" href="/stylesheets/styles.css">
+    <link rel="stylesheet" href="/stylesheets/tag.css">
 </head>
 </head>
 
 
 <body>
 <body>
   <%- include('header') %>
   <%- include('header') %>
 
 
-    <div class="tag-cloud">
-      <% tags.forEach(item=> { %>
-        <a href="/<%= lang %>/tag/<%= item.tag %>" class="tag <%= item.tag == tag ? 'selected' : '' %>" style="color: <%= item.color %>;"><%= item.tag %></a>
-        <% }); %>
-    </div>
+    <h1 style="display: flex; justify-content: center; padding: 10px; color: purple">
+      <%= translate.selectByTag[lang] %>
+    </h1>
 
 
     <div class="content">
     <div class="content">
       <div class="image-grid">
       <div class="image-grid">
         <% data.forEach(item=> { %>
         <% data.forEach(item=> { %>
-          <a href="/<%= item.uri %>"><img src="<%= item.thumb %>" alt="<%= item.title %>"></a>
+          <a href="<%= item.uri %>"><img src="<%= item.thumb %>"
+              alt="<%= item.title %> | <%= translate.printableColoringPage[lang]%>"></a>
           <% }); %>
           <% }); %>
       </div>
       </div>
     </div>
     </div>
 
 
     <%- include('pagination') %>
     <%- include('pagination') %>
 
 
+      <div class="tag-cloud">
+        <% tags.forEach(item=> { %>
+          <a href="/<%= lang %>/tag/<%= item.tag %>" class="tag <%= item.tag == tag ? 'selected' : '' %>"
+            style="color: <%= item.color %>;">
+            <%= item.tag %>
+          </a>
+          <% }); %>
+      </div>
+
 </body>
 </body>
 
 
 </html>
 </html>