iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
Modern Web

在Vibe Coding 時代一起來做沒有AI感的漂亮網站吧!系列 第 27

多語系網站 Next-intl 多行文字處理:到底該用巢狀物件還是陣列?

  • 分享至 

  • xImage
  •  

嗨咿,我是illumi,在使用 Next-intl 做多語系網站時,處理多行文字總是讓人糾結:
每行都要命名好麻煩,有沒有更快的方法?

到底哪種比較好?讓我們用實際案例來分析!

實戰比較

方法 1:語義化巢狀物件

// messages/en.json
{
  "Frontend": {
    "title": "Frontend Engineer",
    "me": "Sonia", 
    "about": {
      "skill": "Good at interactive web pages.",
      "hobby": "I enjoy coding and design.",
    }
  }
}

// 使用方式
const t = await getTranslations('Frontend');
const aboutItems = [
  t('about.skill'),    // ❌ 每一行都要寫一次
  t('about.hobby'),    // ❌ 維護麻煩
];

優點

  • ✅ 語義清楚,一看就知道是什麼內容
  • ✅ 翻譯人員容易理解context
  • ✅ 可以個別控制每一行的顯示

缺點

  • ❌ 程式碼重複,要一個一個寫
  • ❌ 新增內容要修改程式碼
  • ❌ 不適合動態數量的內容

方法 2:編號物件 + 迴圈(推薦!)

// messages/en.json
{
  "Frontend": {
    "title": "Frontend Engineer",
    "me": "Sonia",
    "about": {
      "0": "Good at interactive web pages.",
      "1": "I enjoy coding and design.", ......
    }
  }
}

// 使用方式

const t = await getTranslations('Frontend.about');

// 重點:動態生成陣列
const aboutItems = Array.from({ length: 3 }, (_, i) => t(i.toString()));

// 或者更彈性的版本
const aboutItems = [];
let i = 0;
while (true) {
  try {
    const text = t(i.toString());
    if (!text || text === i.toString()) break; // 沒有翻譯就停止
    aboutItems.push(text);
    i++;
  } catch {
    break;
  }
}

使用範例

export default function Introduction({ title, me }: Props) {
  const t = useTranslations('Frontend.about');
  
  // 一行解決!
  const aboutItems = Array.from({ length: 3 }, (_, i) => t(i.toString()));
  
  return (
    <div>
      <h2>{title}</h2>
      <p>{me}</p>
      <ul className="text-sm">
        {aboutItems.map((item, i) => (
          <li key={i}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

優點

  • ✅ 程式碼簡潔,用迴圈處理
  • ✅ 新增內容不用改程式碼,只改 JSON
  • ✅ 適合動態數量的內容
  • ✅ 容易維護

缺點

  • ❌ 語義性較差,要看內容才知道是什麼
  • ❌ 翻譯人員需要理解編號順序

方法 3:換行字串 + split

// messages/en.json
{
  "Frontend": {
    "about": "Good at interactive web pages.\nI enjoy coding and design.\nThis is the third line of the introduction."
  }
}

// 使用方式
const aboutText = t('Frontend.about');
const aboutItems = aboutText.split('\n');

優點

  • ✅ JSON 結構最簡單
  • ✅ 翻譯人員看到完整段落

缺點

  • ❌ 難以個別控制每一行
  • ❌ 換行符號在不同平台可能有問題
  • ❌ 翻譯工具可能不好處理

結論:選方法2

建立通用的 Hook

// hooks/useArrayTranslations.ts
'use client';
import { useTranslations } from 'next-intl';

export function useArrayTranslations(namespace: string, maxLength: number = 20) {
  const t = useTranslations(namespace);
  
  const items = [];
  for (let i = 0; i < maxLength; i++) {
    try {
      const text = t(i.toString());
      // 如果翻譯不存在,Next-intl 會返回 key 本身
      if (text === i.toString()) break;
      items.push(text);
    } catch {
      break;
    }
  }
  
  return items;
}

Server Component 版本

// utils/getArrayTranslations.ts
import { getTranslations } from 'next-intl/server';

export async function getArrayTranslations(namespace: string, maxLength: number = 20) {
  const t = await getTranslations(namespace);
  
  const items = [];
  for (let i = 0; i < maxLength; i++) {
    try {
      const text = t(i.toString());
      if (text === i.toString()) break;
      items.push(text);
    } catch {
      break;
    }
  }
  
  return items;
}

進階技巧:條件渲染 + 動態內容

// messages/en.json
{
  "skills": {
    "0": "React & Next.js",
    "1": "TypeScript", 
    "2": "Tailwind CSS",
    "premium_0": "Advanced Animation",  // 付費用戶才看得到
    "premium_1": "Performance Optimization"
  }
}
// 進階使用
function SkillsList({ isPremium }: { isPremium: boolean }) {
  const basicSkills = useArrayTranslations('skills');
  const premiumSkills = isPremium ? useArrayTranslations('skills', 'premium_') : [];
  
  const allSkills = [...basicSkills, ...premiumSkills];
  
  return (
    <ul>
      {allSkills.map((skill, i) => (
        <li key={i} className={i >= basicSkills.length ? 'text-gold' : ''}>
          {skill}
        </li>
      ))}
    </ul>
  );
}

效能比較

方法 程式碼維護性 翻譯維護性 執行效能 推薦度
語義化巢狀物件 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
編號物件 + 迴圈 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
換行字串 ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐

好的到這邊啦~我們明天再見~


上一篇
怎麼做多語系網站? 貼上官網安裝方式出 bug? Next.js 15 + next-intl 安裝與使用超完整攻略
下一篇
想做3D?先別急著用Three.js:Spline的使用與為何貼上官網教學都跑不出來物件?
系列文
在Vibe Coding 時代一起來做沒有AI感的漂亮網站吧!30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言