Ứng dụng React / Redux và Đa ngôn ngữ (Quốc tế hóa) - Kiến trúc


119

Tôi đang xây dựng một ứng dụng cần có sẵn bằng nhiều ngôn ngữ và địa phương.

Câu hỏi của tôi không hoàn toàn là kỹ thuật, mà là về kiến ​​trúc, và các mẫu mà mọi người thực sự đang sử dụng trong sản xuất để giải quyết vấn đề này. Tôi không thể tìm thấy bất kỳ "sách dạy nấu ăn" nào cho điều đó, vì vậy tôi đang chuyển sang trang web Hỏi / Đáp yêu thích của mình :)

Đây là những yêu cầu của tôi (chúng thực sự là "tiêu chuẩn"):

  • Người dùng có thể chọn ngôn ngữ (tầm thường)
  • Khi thay đổi ngôn ngữ, giao diện sẽ tự động dịch sang ngôn ngữ đã chọn mới
  • Tôi không quá lo lắng về việc định dạng số, ngày, v.v. vào lúc này, tôi muốn một giải pháp đơn giản để chỉ dịch các chuỗi

Dưới đây là các giải pháp khả thi mà tôi có thể nghĩ ra:

Mỗi thành phần xử lý bản dịch một cách riêng biệt

Điều này có nghĩa là mỗi thành phần có ví dụ một tập hợp các tệp en.json, fr.json, v.v. cùng với nó với các chuỗi đã dịch. Và một chức năng trợ giúp để giúp đọc các giá trị từ những giá trị đó tùy thuộc vào ngôn ngữ đã chọn.

  • Chuyên nghiệp: tôn trọng hơn triết lý React, mỗi thành phần là "độc lập"
  • Nhược điểm: bạn không thể tập trung tất cả các bản dịch trong một tệp (ví dụ: nhờ người khác thêm ngôn ngữ mới)
  • Nhược điểm: bạn vẫn cần phải vượt qua ngôn ngữ hiện tại làm chỗ dựa, trong mọi thành phần máu mủ và con cái của họ

Mỗi thành phần nhận được bản dịch thông qua đạo cụ

Vì vậy, họ không nhận thức được ngôn ngữ hiện tại, họ chỉ lấy một danh sách các chuỗi làm đạo cụ để khớp với ngôn ngữ hiện tại

  • Chuyên nghiệp: vì những chuỗi đó đến "từ đầu", chúng có thể được tập trung ở đâu đó
  • Nhược điểm: Mỗi thành phần hiện được gắn với hệ thống dịch, bạn không thể chỉ sử dụng lại một thành phần, bạn cần chỉ định các chuỗi chính xác mỗi lần

Bạn bỏ qua đạo cụ một chút và có thể sử dụng ngữ cảnh ngẫu nhiên để chuyển tải ngôn ngữ hiện tại

  • Chuyên nghiệp: nó hầu hết trong suốt, không phải chuyển ngôn ngữ hiện tại và / hoặc bản dịch qua đạo cụ
  • Nhược điểm: nó trông cồng kềnh để sử dụng

Nếu bạn có bất kỳ ý tưởng nào khác, xin vui lòng nói!

Bạn làm nó như thế nào?


2
Tôi thích ý tưởng về một đối tượng của các khóa với các chuỗi dịch được truyền lại như một giá đỡ, bạn không cần phải chuyển từng chuỗi làm hỗ trợ riêng lẻ. Thay đổi điều này ở cấp cao nhất sẽ kích hoạt hiển thị lại. Tôi không nghĩ rằng việc sử dụng ngữ cảnh là một ý tưởng hay cho việc này và mỗi thành phần có quyền truy cập vào tệp dịch sẽ khiến chúng bớt "đơ" hơn và thực sự di động trở nên imo (và khó khiến ứng dụng hiển thị lại khi thay đổi ngôn ngữ).
Dominic

1
Trên thực tế, theo facebook.github.io/react/docs/context.html , sử dụng ngữ cảnh để chia sẻ ngôn ngữ hiện tại là một trong những trường hợp sử dụng hợp pháp. Cách tiếp cận tôi đang cố gắng hiện nay là sử dụng này cộng với một cao hơn theo thứ tự phần để thỏa thuận với logic của chiết xuất chuỗi cho rằng thành phần đặc biệt (có thể dựa trên một số key)
Antoine Jaussoin

1
Có lẽ bạn cũng có thể xem qua Instant . Họ giải quyết vấn đề này theo một cách hoàn toàn khác bằng cách giải quyết nó trong frontend ala Optimizely (hay còn gọi là thay đổi DOM trong khi tải).
Marcel Panse

1
Không xấu cả! Nó thực sự là một con thú hoàn toàn khác (ràng buộc bạn với một dịch vụ mà bạn có thể cần trả tiền nếu trang web của bạn phát triển), nhưng tôi thích ý tưởng này và nó thực sự có giá trị đối với một trang web nhỏ mà bạn cần chạy nhanh!
Antoine Jaussoin

4
Ngoài ra, bạn có thể muốn đề cập rằng bạn là một người sáng lập đồng của tức thì, thay vì nói "Họ", nếu như bạn không có bất cứ điều gì để làm với họ :)
Antoine Jaussoin

Câu trả lời:


110

Sau khi thử một số giải pháp, tôi nghĩ rằng tôi đã tìm thấy một giải pháp hoạt động tốt và nên là một giải pháp thành ngữ cho React 0.14 (tức là nó không sử dụng mixin, mà là Các thành phần thứ tự cao hơn) ( chỉnh sửa : tất nhiên cũng hoàn toàn ổn với React 15! ).

Vì vậy, đây là giải pháp, bắt đầu bằng phần dưới cùng (các thành phần riêng lẻ):

Thanh phân

Điều duy nhất mà thành phần của bạn cần (theo quy ước), là một stringsđạo cụ. Nó phải là một đối tượng chứa các chuỗi khác nhau mà Thành phần của bạn cần, nhưng thực sự hình dạng của nó là tùy thuộc vào bạn.

Nó chứa các bản dịch mặc định, vì vậy bạn có thể sử dụng thành phần ở nơi khác mà không cần cung cấp bất kỳ bản dịch nào (nó sẽ hoạt động hiệu quả với ngôn ngữ mặc định, tiếng Anh trong ví dụ này)

import { default as React, PropTypes } from 'react';
import translate from './translate';

class MyComponent extends React.Component {
    render() {

        return (
             <div>
                { this.props.strings.someTranslatedText }
             </div>
        );
    }
}

MyComponent.propTypes = {
    strings: PropTypes.object
};

MyComponent.defaultProps = {
     strings: {
         someTranslatedText: 'Hello World'
    }
};

export default translate('MyComponent')(MyComponent);

Thành phần thứ tự cao hơn

Trên đoạn mã trước, bạn có thể nhận thấy điều này ở dòng cuối cùng: translate('MyComponent')(MyComponent)

translate trong trường hợp này là Thành phần thứ tự cao hơn bao bọc thành phần của bạn và cung cấp một số chức năng bổ sung (cấu trúc này thay thế các hỗn hợp của các phiên bản React trước).

Đối số đầu tiên là một khóa sẽ được sử dụng để tra cứu các bản dịch trong tệp dịch (tôi đã sử dụng tên của thành phần ở đây, nhưng nó có thể là bất kỳ thứ gì). Cái thứ hai (lưu ý rằng chức năng được curryed, để cho phép trình trang trí ES7) là chính Thành phần để bọc.

Đây là mã cho thành phần dịch:

import { default as React } from 'react';
import en from '../i18n/en';
import fr from '../i18n/fr';

const languages = {
    en,
    fr
};

export default function translate(key) {
    return Component => {
        class TranslationComponent extends React.Component {
            render() {
                console.log('current language: ', this.context.currentLanguage);
                var strings = languages[this.context.currentLanguage][key];
                return <Component {...this.props} {...this.state} strings={strings} />;
            }
        }

        TranslationComponent.contextTypes = {
            currentLanguage: React.PropTypes.string
        };

        return TranslationComponent;
    };
}

Nó không phải là phép thuật: nó sẽ chỉ đọc ngôn ngữ hiện tại từ ngữ cảnh (và ngữ cảnh đó không chảy ra khắp cơ sở mã, chỉ được sử dụng ở đây trong trình bao bọc này), và sau đó lấy đối tượng chuỗi có liên quan từ các tệp được tải. Phần logic này khá đơn giản trong ví dụ này, có thể được thực hiện theo cách bạn thực sự muốn.

Phần quan trọng là nó lấy ngôn ngữ hiện tại từ ngữ cảnh và chuyển đổi ngôn ngữ đó thành chuỗi, với khóa được cung cấp.

Ở trên cùng của hệ thống phân cấp

Trên thành phần gốc, bạn chỉ cần đặt ngôn ngữ hiện tại từ trạng thái hiện tại của mình. Ví dụ sau đây đang sử dụng Redux như một triển khai giống như Flux, nhưng nó có thể dễ dàng được chuyển đổi bằng cách sử dụng bất kỳ khuôn khổ / mẫu / thư viện nào khác.

import { default as React, PropTypes } from 'react';
import Menu from '../components/Menu';
import { connect } from 'react-redux';
import { changeLanguage } from '../state/lang';

class App extends React.Component {
    render() {
        return (
            <div>
                <Menu onLanguageChange={this.props.changeLanguage}/>
                <div className="">
                    {this.props.children}
                </div>

            </div>

        );
    }

    getChildContext() {
        return {
            currentLanguage: this.props.currentLanguage
        };
    }
}

App.propTypes = {
    children: PropTypes.object.isRequired,
};

App.childContextTypes = {
    currentLanguage: PropTypes.string.isRequired
};

function select(state){
    return {user: state.auth.user, currentLanguage: state.lang.current};
}

function mapDispatchToProps(dispatch){
    return {
        changeLanguage: (lang) => dispatch(changeLanguage(lang))
    };
}

export default connect(select, mapDispatchToProps)(App);

Và để kết thúc, các tệp dịch:

Tệp dịch

// en.js
export default {
    MyComponent: {
        someTranslatedText: 'Hello World'
    },
    SomeOtherComponent: {
        foo: 'bar'
    }
};

// fr.js
export default {
    MyComponent: {
        someTranslatedText: 'Salut le monde'
    },
    SomeOtherComponent: {
        foo: 'bar mais en français'
    }
};

các bạn nghĩ sao?

Tôi nghĩ rằng nó giải quyết được tất cả vấn đề mà tôi đang cố gắng tránh trong câu hỏi của mình: logic dịch không tràn ra toàn bộ mã nguồn, nó khá cô lập và cho phép sử dụng lại các thành phần mà không có nó.

Ví dụ: MyComponent không cần phải được bao bọc bởi translate () và có thể tách biệt, cho phép sử dụng lại nó bởi bất kỳ ai khác muốn cung cấp stringstheo cách riêng của họ.

[Chỉnh sửa: 31/03/2016]: Gần đây tôi đã làm việc trong Hội đồng hồi cứu (cho Agile Retrospectives), được xây dựng bằng React & Redux và đa ngôn ngữ. Vì khá nhiều người đã yêu cầu một ví dụ thực tế trong các bình luận, đây là:

Bạn có thể tìm thấy mã ở đây: https://github.com/antoinejaussoin/retro-board/tree/master


Đây là một giải pháp tuyệt vời .. tự hỏi nếu bạn vẫn tiếp tục với điều này sau một vài tháng? Tôi đã không tìm thấy lời khuyên nhiều trong cách tư vấn về mô hình trực tuyến này
Damon

2
Tôi thực sự thấy rằng nó hoạt động hoàn hảo (cho nhu cầu của tôi). Nó làm cho công việc thành phần mà không dịch theo mặc định, và dịch chỉ đến trên đầu trang của nó mà không có các thành phần được nhận thức của nó
Antoine Jaussoin

1
@ l.cetinsoy bạn có thể sử dụng giá dangerouslySetInnerHTMLđỡ, chỉ cần lưu ý đến các tác động (làm sạch đầu vào theo cách thủ công). Xem facebook.github.io/react/tips/dangerously-set-inner-html.html
Teodor Sandu

6
Có lý do gì khiến bạn chưa thử react-intl không?
SureshCS

1
Thực sự thích giải pháp này. Một điều tôi muốn thêm mà chúng tôi tìm thấy rất hữu ích cho ổn định và tiết kiệm thời gian là nếu bạn có rất nhiều thành phần với chuỗi thông thường bạn có thể tận dụng lợi thế của các biến và lan rộng ra các đối tượng ví dụconst formStrings = { cancel, create, required }; export default { fooForm: { ...formStrings, foo: 'foo' }, barForm: { ...formStrings, bar: 'bar' } }
Huw Davies

18

Theo kinh nghiệm của tôi, cách tốt nhất là tạo trạng thái redux i18n và sử dụng nó, vì nhiều lý do:

1- Điều này sẽ cho phép bạn chuyển giá trị ban đầu từ cơ sở dữ liệu, tệp cục bộ hoặc thậm chí từ công cụ mẫu như EJS hoặc jade

2- Khi người dùng thay đổi ngôn ngữ, bạn có thể thay đổi toàn bộ ngôn ngữ ứng dụng mà không cần làm mới giao diện người dùng.

3- Khi người dùng thay đổi ngôn ngữ, điều này cũng sẽ cho phép bạn truy xuất ngôn ngữ mới từ API, tệp cục bộ hoặc thậm chí từ các hằng số

4- Bạn cũng có thể lưu những thứ quan trọng khác với các chuỗi như múi giờ, đơn vị tiền tệ, hướng (RTL / LTR) và danh sách các ngôn ngữ có sẵn

5- Bạn có thể xác định ngôn ngữ thay đổi như một hành động redux thông thường

6- Bạn có thể đặt các chuỗi phụ trợ và giao diện người dùng của mình ở một nơi, ví dụ: trong trường hợp của tôi, tôi sử dụng i18n-node để bản địa hóa và khi người dùng thay đổi ngôn ngữ giao diện người dùng, tôi chỉ thực hiện một lệnh gọi API bình thường và trong phần phụ trợ, tôi chỉ quay lại i18n.getCatalog(req)điều này sẽ trả về tất cả các chuỗi người dùng chỉ cho ngôn ngữ hiện tại

Đề xuất của tôi cho trạng thái ban đầu của i18n là:

{
  "language":"ar",
  "availableLanguages":[
    {"code":"en","name": "English"},
    {"code":"ar","name":"عربي"}
  ],
  "catalog":[
     "Hello":"مرحباً",
     "Thank You":"شكراً",
     "You have {count} new messages":"لديك {count} رسائل جديدة"
   ],
  "timezone":"",
  "currency":"",
  "direction":"rtl",
}

Các mô-đun hữu ích bổ sung cho i18n:

1- chuỗi-mẫu này sẽ cho phép bạn đưa các giá trị vào giữa các chuỗi danh mục của mình, ví dụ:

import template from "string-template";
const count = 7;
//....
template(i18n.catalog["You have {count} new messages"],{count}) // لديك ٧ رسائل جديدة

2- định dạng con người, mô-đun này sẽ cho phép bạn chuyển đổi một số thành / từ một chuỗi con người có thể đọc được, ví dụ:

import humanFormat from "human-format";
//...
humanFormat(1337); // => '1.34 k'
// you can pass your own translated scale, e.g: humanFormat(1337,MyScale)

3- momentj là thư viện npm ngày và giờ nổi tiếng nhất, bạn có thể dịch thời điểm nhưng nó đã có sẵn bản dịch chỉ cần bạn chuyển ngôn ngữ trạng thái hiện tại, ví dụ:

import moment from "moment";

const umoment = moment().locale(i18n.language);
umoment.format('MMMM Do YYYY, h:mm:ss a'); // أيار مايو ٢ ٢٠١٧، ٥:١٩:٥٥ م

Cập nhật (14/06/2019)

Hiện tại, có nhiều khung công tác triển khai cùng một khái niệm bằng cách sử dụng API ngữ cảnh phản ứng (không có redux), cá nhân tôi đã đề xuất I18next


Cách tiếp cận này có hiệu quả với hơn hai ngôn ngữ không? Xem xét việc thiết lập danh mục
tempranova

Xuống bình chọn. Điều này không trả lời câu hỏi. OP yêu cầu một ý tưởng kiến ​​trúc, không phải đề xuất hoặc so sánh bất kỳ thư viện i18n nào.
TrungDQ

9
Tôi đã đề xuất danh mục i18n là trạng thái redux, có vẻ như bạn không hiểu redux
Fareed Alnamrouti

5

Giải pháp của Antoine hoạt động tốt, nhưng có một số lưu ý:

  • Nó sử dụng ngữ cảnh React trực tiếp, điều mà tôi có xu hướng tránh khi đã sử dụng Redux
  • Nó nhập trực tiếp các cụm từ từ một tệp, có thể có vấn đề nếu bạn muốn tìm nạp ngôn ngữ cần thiết trong thời gian chạy, phía máy khách
  • Nó không sử dụng bất kỳ thư viện i18n nào, có dung lượng nhẹ, nhưng không cung cấp cho bạn quyền truy cập vào các chức năng dịch tiện dụng như đa phương hóa và nội suy

Đó là lý do tại sao chúng tôi đã xây dựng redux-polyglot trên cả Redux và AirBNB's Polyglot .
(Tôi là một trong những tác giả)

Nó cung cấp :

  • một trình rút gọn để lưu trữ ngôn ngữ và thông điệp tương ứng trong cửa hàng Redux của bạn. Bạn có thể cung cấp cả hai bằng cách:
    • một phần mềm trung gian mà bạn có thể cấu hình để bắt hành động cụ thể, loại trừ ngôn ngữ hiện tại và nhận / tìm nạp các thông báo liên quan.
    • cử trực tiếp setLanguage(lang, messages)
  • một getP(state)bộ chọn truy xuất một Pđối tượng hiển thị 4 phương thức:
    • t(key): hàm T đa giác ban đầu
    • tc(key): bản dịch viết hoa
    • tu(key): bản dịch nghiêng về phía trên
    • tm(morphism)(key): bản dịch được biến đổi tùy chỉnh
  • một getLocale(state)bộ chọn để lấy ngôn ngữ hiện tại
  • một translatethành phần bậc cao hơn để nâng cao các thành phần React của bạn bằng cách đưa pđối tượng vào các đạo cụ

Ví dụ sử dụng đơn giản:

gửi ngôn ngữ mới:

import setLanguage from 'redux-polyglot/setLanguage';

store.dispatch(setLanguage('en', {
    common: { hello_world: 'Hello world' } } }
}));

trong thành phần:

import React, { PropTypes } from 'react';
import translate from 'redux-polyglot/translate';

const MyComponent = props => (
  <div className='someId'>
    {props.p.t('common.hello_world')}
  </div>
);
MyComponent.propTypes = {
  p: PropTypes.shape({t: PropTypes.func.isRequired}).isRequired,
}
export default translate(MyComponent);

Vui lòng cho tôi biết nếu bạn có bất kỳ câu hỏi / gợi ý nào!


1
Rất nhiều cụm từ gốc được dịch tốt hơn. Và để tạo một công cụ phân tích cú pháp tất cả các thành phần cho các _()hàm chẳng hạn để lấy tất cả các chuỗi đó. Vì vậy, bạn có thể dịch tệp ngôn ngữ dễ dàng hơn và không bị rối với các biến số điên rồ. Trong một số trường hợp, các trang đích cần một phần cụ thể của bố cục được hiển thị khác nhau. Vì vậy, một số chức năng thông minh về cách chọn mặc định so với các lựa chọn khả thi khác cũng sẽ có sẵn.
Roman M. Koss

Xin chào @Jalil, có bất kỳ ví dụ hoàn chỉnh nào với phần mềm trung gian không?
ArkadyB

Xin chào @ArkadyB, Chúng tôi sử dụng nó để sản xuất trên một số dự án không có nguồn mở. Bạn có thể tìm thêm thông tin về mô-đun README: npmjs.com/package/redux-polyglot Bạn có thắc mắc / gặp khó khăn gì khi sử dụng nó không?
Jalil

Vấn đề chính của tôi với điều này và polyglot.js là nó hoàn toàn đang phát minh lại bánh xe thay vì xây dựng trên các tệp PO. Thư viện thay thế này có vẻ đầy hứa hẹn npmjs.com/package/redux-i18n . Tôi không nghĩ điều đó làm khác đi nhiều - nó chỉ cung cấp thêm một lớp để chuyển đổi sang và từ các tệp PO.
icc97

2

Từ nghiên cứu của tôi về vấn đề này, dường như có hai cách tiếp cận chính được sử dụng cho i18n trong JavaScript, ICUgettext .

Tôi chỉ sử dụng gettext, vì vậy tôi có thành kiến.

Điều làm tôi ngạc nhiên là mức độ hỗ trợ kém như thế nào. Tôi đến từ thế giới PHP, CakePHP hoặc WordPress. Trong cả hai trường hợp đó, đó là một tiêu chuẩn cơ bản mà tất cả các chuỗi được bao quanh một cách đơn giản __(''), sau đó xuống dòng sâu hơn, bạn nhận được bản dịch bằng cách sử dụng tệp PO rất dễ dàng.

gettext

Bạn sẽ làm quen với sprintf để định dạng chuỗi và các tệp PO sẽ được dịch dễ dàng bởi hàng ngàn cơ quan khác nhau.

Có hai tùy chọn phổ biến:

  1. i18next , với cách sử dụng được mô tả bởi bài đăng trên blog arkency.com này
  2. Jed , với cách sử dụng được mô tả bởi bài đăng sentry.iobài đăng React + Redux này ,

Cả hai đều có hỗ trợ kiểu gettext, định dạng kiểu sprintf của chuỗi và nhập / xuất sang tệp PO.

i18next có một phần mở rộng React do chính họ phát triển. Jed không. Sentry.io dường như sử dụng tích hợp tùy chỉnh của Jed với React. Các React + Redux , đề xuất sử dụng

Công cụ: jed + po2json + jsxgettext

Tuy nhiên, Jed có vẻ giống như một triển khai tập trung vào gettext hơn - đó là nó được bày tỏ ý định, trong đó i18next chỉ có nó như một tùy chọn.

ICU

Điều này hỗ trợ nhiều hơn cho các trường hợp biên xung quanh bản dịch, ví dụ như đối với vấn đề giới tính. Tôi nghĩ rằng bạn sẽ thấy những lợi ích từ điều này nếu bạn có nhiều ngôn ngữ phức tạp hơn để dịch sang.

Một tùy chọn phổ biến cho việc này là messageformat.js . Thảo luận ngắn gọn trong hướng dẫn blog sentry.io này . messageformat.js thực sự được phát triển bởi cùng một người đã viết Jed. Anh ấy tuyên bố khá rõ ràng về việc sử dụng ICU :

Jed là tính năng hoàn chỉnh theo ý kiến ​​của tôi. Tôi rất vui khi được sửa lỗi, nhưng nói chung là không quan tâm đến việc thêm nhiều hơn vào thư viện.

Tôi cũng duy trì messageformat.js. Nếu bạn không đặc biệt cần triển khai gettext, tôi có thể đề xuất sử dụng MessageFormat thay thế, vì nó hỗ trợ tốt hơn cho số nhiều / giới tính và có dữ liệu ngôn ngữ tích hợp.

So sánh thô sơ

gettext với sprintf:

i18next.t('Hello world!');
i18next.t(
    'The first 4 letters of the english alphabet are: %s, %s, %s and %s', 
    { postProcess: 'sprintf', sprintf: ['a', 'b', 'c', 'd'] }
);

messageformat.js (phỏng đoán tốt nhất của tôi khi đọc hướng dẫn ):

mf.compile('Hello world!')();
mf.compile(
    'The first 4 letters of the english alphabet are: {s1}, {s2}, {s3} and {s4}'
)({ s1: 'a', s2: 'b', s3: 'c', s4: 'd' });

Xuống bình chọn. Điều này không trả lời câu hỏi. OP yêu cầu một ý tưởng kiến ​​trúc, không phải đề xuất hoặc so sánh bất kỳ thư viện i18n nào.
TrungDQ

@TrungDQ Đây là những gì OP đã hỏi: "Câu hỏi của tôi không hoàn toàn là kỹ thuật, mà là về kiến ​​trúc và các mẫu mà mọi người thực sự đang sử dụng trong sản xuất để giải quyết vấn đề này." . Đây là hai mẫu đang được sử dụng trong sản xuất.
icc97

Theo tôi câu trả lời này không cung cấp thông tin mà tôi (và những người khác) đang tìm kiếm. Thông tin bạn cung cấp là hữu ích, nhưng có thể cho một câu hỏi khác. Tôi chỉ muốn đóng góp phiếu phản đối của mình để làm cho câu trả lời đúng bật lên trên cùng (tôi hy vọng).
TrungDQ

@TrungDQ Nếu đó không phải là thứ bạn đang tìm kiếm, thì chỉ cần tán thành câu bạn đã sử dụng và bỏ qua những câu khác chứ không phải từ chối những câu trả lời hoàn toàn hợp lệ không khớp với phần cụ thể của câu hỏi mà bạn quan tâm.
icc97

1

Nếu vẫn chưa hoàn thành, hãy xem https://react.i18next.com/ có thể là một lời khuyên tốt. Nó dựa trên i18next: học một lần - dịch mọi nơi.

Mã của bạn sẽ trông giống như sau:

<div>{t('simpleContent')}</div>
<Trans i18nKey="userMessagesUnread" count={count}>
  Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message. <Link to="/msgs">Go to messages</Link>.
</Trans>

Đi kèm với các mẫu cho:

  • webpack
  • cra
  • expo.js
  • next.js
  • tích hợp sách truyện
  • mưa rào
  • dat
  • ...

https://github.com/i18next/react-i18next/tree/master/example

Bên cạnh đó, bạn cũng nên xem xét quy trình làm việc trong quá trình phát triển và sau này cho người dịch của bạn -> https://www.youtube.com/watch?v=9NOzJhgmyQE


Điều này không trả lời câu hỏi. OP yêu cầu một ý tưởng kiến ​​trúc, không phải đề xuất hoặc so sánh bất kỳ thư viện i18n nào.
TrungDQ

@TrungDQ cũng như nhận xét của bạn về câu trả lời của tôi mà bạn đã phản đối - OP đã yêu cầu các giải pháp hiện tại được sử dụng trong sản xuất. Tuy nhiên, tôi đã đề xuất i18next trong câu trả lời của mình từ hồi tháng 2
icc97

0

Tôi muốn đề xuất một giải pháp đơn giản bằng cách sử dụng create-react-app .

Ứng dụng sẽ được xây dựng cho mọi ngôn ngữ riêng biệt, do đó toàn bộ logic dịch sẽ được chuyển ra khỏi ứng dụng.

Máy chủ web sẽ tự động phân phát ngôn ngữ chính xác, tùy thuộc vào tiêu đề Ngôn ngữ chấp nhận hoặc theo cách thủ công bằng cách đặt cookie .

Hầu hết, chúng tôi không thay đổi ngôn ngữ nhiều lần, nếu có)

Dữ liệu dịch được đặt bên trong cùng một tệp thành phần, sử dụng nó, cùng với các kiểu, html và mã.

Và ở đây chúng tôi có thành phần hoàn toàn độc lập chịu trách nhiệm về trạng thái, chế độ xem, bản dịch của chính nó:

import React from 'react';
import {withStyles} from 'material-ui/styles';
import {languageForm} from './common-language';
const {REACT_APP_LANGUAGE: LANGUAGE} = process.env;
export let language; // define and export language if you wish
class Component extends React.Component {
    render() {
        return (
            <div className={this.props.classes.someStyle}>
                <h2>{language.title}</h2>
                <p>{language.description}</p>
                <p>{language.amount}</p>
                <button>{languageForm.save}</button>
            </div>
        );
    }
}
const styles = theme => ({
    someStyle: {padding: 10},
});
export default withStyles(styles)(Component);
// sets laguage at build time
language = (
    LANGUAGE === 'ru' ? { // Russian
        title: 'Транзакции',
        description: 'Описание',
        amount: 'Сумма',
    } :
    LANGUAGE === 'ee' ? { // Estonian
        title: 'Tehingud',
        description: 'Kirjeldus',
        amount: 'Summa',
    } :
    { // default language // English
        title: 'Transactions',
        description: 'Description',
        amount: 'Sum',
    }
);

Thêm biến môi trường ngôn ngữ vào package.json của bạn

"start": "REACT_APP_LANGUAGE=ru npm-run-all -p watch-css start-js",
"build": "REACT_APP_LANGUAGE=ru npm-run-all build-css build-js",

Đó là nó!

Ngoài ra, câu trả lời ban đầu của tôi bao gồm cách tiếp cận nguyên khối hơn với tệp json duy nhất cho mỗi bản dịch:

lang / ru.json

{"hello": "Привет"}

lib / lang.js

export default require(`../lang/${process.env.REACT_APP_LANGUAGE}.json`);

src / App.jsx

import lang from '../lib/lang.js';
console.log(lang.hello);

Nó sẽ không chỉ hoạt động tại thời điểm biên dịch? Nếu người dùng không có khả năng thay đổi ngôn ngữ một cách nhanh chóng? Đó sẽ là một trường hợp sử dụng khác.
Antoine Jaussoin

Ứng dụng sẽ được biên dịch cho mọi ngôn ngữ cần thiết. Máy chủ web sẽ tự động phân phát phiên bản chính xác, tùy thuộc vào tiêu đề "Ngôn ngữ chấp nhận" hoặc theo cookie do người dùng đặt khi đang di chuyển. Bằng cách này, toàn bộ logic dịch có thể được chuyển ra khỏi ứng dụng.
Igor Sukharev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.