DEV Community

Cover image for 如何國際化(i18n)您的Preact項目
Henry Lim
Henry Lim

Posted on • Originally published at Medium

2 1

如何國際化(i18n)您的Preact項目

🇺🇸 English Version (英文版): https://dev.to/henrylim96/how-to-add-internationalization-i18n-to-your-preact-application-5gd6


什麼是國際化 (i18n)?

國際化 (Internationalization),也被稱為i18n,意思指i和n之间有18个字母。國際化是指修改軟體使之能適應目標市場的語言、地區差異以及技術需要。

在這篇文章中,我們將會使用preact-i18n來國際化您的Preact項目。


步驟 1:設置Preact CLI, 並創建一個新的項目

注: 如果您已經熟悉Preact了,您可以跳到下一步。

如果您還沒有將Preact CLI安裝到您的電腦,請使用以下的命令來安裝Preact CLI。這CLI需要Node.js 版本 6.x 或以上。

$ npm install -g preact-cli

當您已經成功將Preact CLI安裝到您的電腦中,我們將會使用以下的命令來創建一個名為my-project的項目。在這個項目中,我們將會使用default模板。

$ preact create default my-project

之後呢,您可以使用以下的命令來啟動本地測試服務器。

$ cd my-project && npm run start

這個時候,我們需要打開我們的遊覽器,並前往http://localhost:8080, 你將會看到像這樣類似的畫面:

步驟 2:安裝preact-18n

我們將會使用以下的命令來安裝preact-i18n到您的項目中。

$ npm install --save preact-i18n

preact-i18n是非常容易使用的。更重要的是, 這preact-i18n在gzip之後才佔據不到1.3kb的大小。

步驟 3:創建definition文件

當你已將preact-i18n安裝到您的項目之後,我們將會創建一個definition文件。我們將會把我們要翻譯的文字和句子,儲存在這個JSON文件中。

我們將會把這個definition文件儲存在src/i18n/zh-tw.json

{ 
  "home": {
    "title": "主頁", 
    "text": "這是個Home組件。"
  }
}
Enter fullscreen mode Exit fullscreen mode

步驟 4:導入IntlProvider及definition文件

接下來,我們將會從src/components中打開app.js。我們將會在這個文件中導入IntlProviderdefinition文件。

import { IntlProvider } from 'preact-i18n';
import definition from '../i18n/zh-tw.json';
Enter fullscreen mode Exit fullscreen mode

步驟 5:把IntlProvider放在項目中最高層級的組件

然後呢,我們將會在把<IntlProvider>放在項目中最高層級的組件,也就是我們的app.js。這樣子,我們就能在這Preact項目中的任何一個組件中讀取到definition文件。

render() {
  return(
    <IntlProvider definition={definition}>
      <div id="app" />
    </IntlProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

在這個時候,您的app.js文件的內容應該是要跟以下的例子類似:

import { h, Component } from 'preact';
import { Router } from 'preact-router';
import Header from './header';
import Home from '../routes/home';
import Profile from '../routes/profile';
// 導入 IntlProvider 及 definition 文件。
import { IntlProvider } from 'preact-i18n';
import definition from '../i18n/zh-tw.json';
export default class App extends Component {

 handleRoute = e => {
  this.currentUrl = e.url;
 };
render() {
  return (
   // 把 <IntlProvider> 放在項目中最高層級的組件
   <IntlProvider definition={definition}>
    <div id="app">
     <Header />
     <Router onChange={this.handleRoute}>
      <Home path="/" />
      <Profile path="/profile/" user="me" />
      <Profile path="/profile/:user" />
     </Router>
    </div>
   </IntlProvider>
  );
 }
}
Enter fullscreen mode Exit fullscreen mode

步驟 6:使用Text來顯示翻譯字符串文字

我們只差一步就成功了。在以下的例子中,我們將會翻譯主頁(src/routes/home/index.js)中所有的文字。現在,我們只需要把網頁中的字改成<Text>。因此,我們將會把<Text>添加進<h1><p>裡。

import { Text } from 'preact-i18n';
const Home = () => ( 
  <div> 
    <h1> 
      <Text id="home.title">Home</Text> 
    </h1> 
    <p> 
      <Text id="home.text">This is the Home component.</Text> 
    </p> 
  </div> 
); 
export default Home;
Enter fullscreen mode Exit fullscreen mode

後備文字

為了避免網頁中出現空白,我們應該在<Text>中輸入後備文字。 如果preact-i18n無法在您的definition中找到相關的文字或句子,那preact-i18n將會使用你剛才在 <Text>…</Text>輸入的後備文字。

<Text id="unknown.definition">This is a fallback text.</Text>
// 這將會渲染: "This is a fallback text."
Enter fullscreen mode Exit fullscreen mode

Localizer 和 MarkupText

如果您是想要翻譯HTML屬性中的文字 (比如說 placeholder=""或是title=""等等),您應該使用<Localizer>,而並不是使用<Text>

相反的,如果您是想要在您的翻譯的文字或句子中使用HTML Markup, 您必須使用<MarkupText><MarkupText>將會把已翻譯好的文字或句子渲染在一個<span>tag中。

在以下的例子中,我們將會在我們的definition文件中添加多幾行的代碼。first_namelast_name,將會使用在<Localizer>中的例子。 而我們會在<MarkupText>中的例子使用link

{ 
  "first_name": "名",
  "last_name": "姓",
  "link": "這是個<a href='https://www.google.com'>連結</a>"
}
Enter fullscreen mode Exit fullscreen mode

在你更新主頁(src/routes/home/index.js)中的內容之前,記得將LocalizerMarkupText導入到該頁中:

import { Text, Localizer, MarkupText } from 'preact-i18n';
const Home = () => ( 
  <div> 
    <Localizer> 
      <input placeholder={<Text id="first_name" />} /> 
    </Localizer> 
    <Localizer> 
      <input placeholder={<Text id="last_name" />} /> 
    </Localizer> 
    <MarkupText id="link"> 
      This is a <a href="https://www.google.com">link</a>
    </MarkupText>
  </div>
);
export default Home;
Enter fullscreen mode Exit fullscreen mode

模板 (Templating)

如果您想要在您的definition中注入一些自定義的字符串,您可以使用fields屬性來實現。

首先呢,我們需要先更新我們的definition文件。在我們的definition文件中,我們需要將我們要被自定義的字符串替代的文字,更改成像{{count}}或者是{{total}}這樣子的佔位符。

{
  "page": "{{count}} / {{total}} 頁"
}
Enter fullscreen mode Exit fullscreen mode

之後呢,我們需要在我們的<Text />中加入fields屬性。因此,您的代碼應如下所示:

import { Text } from 'preact-i18n'; 
const Home = () => ( 
  <div>
    <h2> 
      <Text id="page" fields={{ count: 5, total: 10 }}>
         5 / 10 Pages
      </Text> 
    </h2> 
  </div> 
); 
export default Home;
Enter fullscreen mode Exit fullscreen mode

复数 (Pluralization)

如果您要翻譯的語言有复数的話(比如說像英文:apple / apples),您可以使用以下其中一個方法,來把已翻譯好的文字和句子放進您的definition文件裡。

  • "key": { "singular":"apple", "plural":"apples" }
  • "key": { "none":"no apples", "one":"apple", "many":"apples" }
  • "key": ["apples", "apple"]

在以下的例子中,我們將會把模板和复数的例子結合在一起。但在那之前,我們需要更新我們的definition文件:

{
  "apple": { 
    "singular": "Henry has {{count}} apple.", 
    "plural":"Henry has {{count}} apples." 
  }
}
Enter fullscreen mode Exit fullscreen mode

接著,我們將會把以下的代碼粘貼到src/routes/home/index.js中:

import { Text } from 'preact-i18n'; 
const Home = () => ( 
  <div> 
    <p> 
      <Text id="apple" plural={1} fields={{ count: 1 }} /> 
    </p> 
    <p> 
      <Text id="apple" plural={100} fields={{ count: 100 }} /> 
    </p> 
  </div> 
); 
export default Home;
Enter fullscreen mode Exit fullscreen mode

根據以上的步驟,你就能在您的Preact項目中使用模板和复数。


動態導入definition文件

在現實情況中,您將會根據用戶的選擇來設定網頁的語言。

您可以使用遊覽器的語言(通過navigator.language), 或者是讓用戶自己手動更換語言。

然而,為了避免我們將不必要的definition文件導入進去,我們可以使用import()來實現動態導入definition文件。這樣一來,我們只會導入用戶所選擇的語言所需要的definition文件。

import { Component } from 'preact'; 
import { IntlProvider } from 'preact-i18n'; 
import defaultDefinition from '../i18n/zh-tw.json'; 
export default class App extends Component { 
  state = { 
    definition: defaultDefinition 
  } 
  changeLanguage = (lang) => { 
    // 我們可以使用這個函數來更換語言
    import(`../i18n/${lang}.json`) 
      .then(definition => this.setState({ definition })); 
  }; 
  render({ }, { definition }) { 
    return ( 
      <IntlProvider definition={definition}> 
        <div id="app" /> 
      </IntlProvider> 
    ); 
  } 
}
Enter fullscreen mode Exit fullscreen mode

根據以上的例子,我們可以使用這函數:this.changeLanguage("zh-TW") 來導入definition文件並更改網頁的語言。


誰在使用preact-i18n?

我自己的業餘項目: Remote for Slides,正在使用著preact-i18n

Remote for Slides是一個漸進式網絡應用程序(PWA) + Chrome 擴充器。這能讓用戶在任何設備上,遠程遙控Google簡報。是時候跟昂貴的翻頁筆說再見了。

Remote for Slides 漸進式網絡應用程序支持多達8個語言,包括了英文、繁體中文、簡體中文、加泰羅尼亞文、西班牙文、 法文、波蘭文、以及Euskera。

在這個項目中,我也使用了我在剛才提到的 "動態導入definition文件" 的方法。這可以避免應用程序導入一些沒使用到的definition文件。這將會提升應用程序性能。

除此之外,Remote for Slides 漸進式網絡應用程序也將會自動地設置語言。這應用程序將會根據遊覽器的語言(navigator.language)、或者是根據URL中的parameter (ie: s.limhenry.xyz/?hl=zh-tw)來更改語言。 當然,用戶也可以從設置中更改語言。

您可以在這裡知道關於Remote for Slides更多的訊息:


更多資源

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

SurveyJS custom survey software

Build Your Own Forms without Manual Coding

SurveyJS UI libraries let you build a JSON-based form management system that integrates with any backend, giving you full control over your data with no user limits. Includes support for custom question types, skip logic, an integrated CSS editor, PDF export, real-time analytics, and more.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay