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

🇺🇸 English Version (英文版): https://medium.com/@limhenry/how-to-add-i18n-to-preact-dff1bf19917

什麼是國際化 (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組件。"
}
}

步驟 4:導入IntlProvider及definition文件

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

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

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

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

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

在這個時候,您的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>
);
}
}

步驟 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;

後備文字

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

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

<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>"
}

在你更新主頁(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;

模板 (Templating)

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

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

{
"page": "{{count}} / {{total}} 頁"
}

之後呢,我們需要在我們的<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;

复数 (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."
}
}

接著,我們將會把以下的代碼粘貼到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;

根據以上的步驟,你就能在您的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>
);
}
}

根據以上的例子,我們可以使用這函數: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 漸進式網絡應用程序 (繁體中文、西班牙文、波蘭文)

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

--

--

--

Google Developer Expert in Web Technologies | Front-end Web Developer | Making the web better with Preact, Polymer, Web Components, PWA & Firebase

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Henry Lim

Henry Lim

Google Developer Expert in Web Technologies | Front-end Web Developer | Making the web better with Preact, Polymer, Web Components, PWA & Firebase

More from Medium

All About Prototypes in JS

Types of Browser’s storage

Front-End Development with Svelte: Basics Walkthrough with Examples

Transpiler vs Compiler