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

henrylim96

Henry Lim

Posted on June 18, 2019

如何國際化(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更多的訊息:


更多資源

💖 💪 🙅 🚩
henrylim96
Henry Lim

Posted on June 18, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related