2011-06-23 41 views
8

Tôi đã được giao nhiệm vụ giới thiệu i18n cho ứng dụng web J2EE bằng cách sử dụng đặc tả kỹ thuật 2.3 servlet. Ứng dụng này rất lớn và đã được phát triển tích cực trong hơn 8 năm.Ứng dụng web Java i18n

Vì vậy, tôi muốn có được điều đúng ngay từ đầu để tôi có thể giới hạn thời gian tôi cần phải thu thập dữ liệu thông qua JSP, tệp JavaScript, servlet và bất cứ nơi nào khác, thay thế chuỗi được mã hóa cứng bằng các giá trị từ gói tin nhắn.

Không có khung nào được sử dụng tại đây. Làm thế nào tôi có thể tiếp cận hỗ trợ i18n. Lưu ý rằng tôi muốn có một JSP duy nhất cho mỗi lượt xem tải văn bản từ (a) tệp thuộc tính và không phải là một JSP khác nhau cho từng ngôn ngữ được hỗ trợ.

Tôi đoán câu hỏi chính của mình là liệu tôi có thể đặt ngôn ngữ ở đâu đó trong 'phụ trợ' (tức là đọc ngôn ngữ từ hồ sơ người dùng trên đăng nhập và giá trị lưu trữ trong phiên) và sau đó hy vọng rằng các trang JSP sẽ có thể tải chính xác chuỗi được chỉ định từ tệp thuộc tính chính xác (ví dụ: từ messages_fr.properties khi ngôn ngữ là tiếng Pháp) trái với việc thêm logic để tìm miền địa phương chính xác trong mỗi JSP.

Bất kỳ ý tưởng nào về cách tôi có thể tiếp cận điều này?

+0

bản sao có thể có của [Cách quốc tế hóa ứng dụng web java.] (Http://stackoverflow.com/questions/4276061/how-to-internationalize-a-java-web-application) – BalusC

Trả lời

18

Có rất nhiều thứ cần phải được đưa về chăm sóc trong khi quốc tế hóa ứng dụng:

phát hiện Locale

Điều đầu tiên bạn cần phải suy nghĩ về là để phát hiện Locale của người dùng cuối. Tùy thuộc vào những gì bạn muốn hỗ trợ nó có thể dễ dàng hoặc một chút phức tạp.

  1. Như bạn đã biết, trình duyệt web có xu hướng gửi ngôn ngữ ưa thích của người dùng cuối thông qua tiêu đề Ngôn ngữ chấp nhận HTTP. Việc truy cập thông tin này trong Servlet có thể đơn giản như gọi số request.getLocale().Nếu bạn không có kế hoạch hỗ trợ bất kỳ ưa thích Locale Detection workflow, bạn có thể chỉ cần dính vào phương pháp này.
  2. Nếu bạn có Hồ sơ người dùng trong ứng dụng của mình, bạn có thể muốn thêm Ngôn ngữ ưa thích và Ngôn ngữ định dạng ưa thích vào nó. Trong trường hợp này, bạn cần phải chuyển miền địa phương sau khi người dùng đăng nhập.
  3. Bạn có thể muốn hỗ trợ chuyển đổi ngôn ngữ dựa trên URL (ví dụ: http://deutsch.example.com/ hoặc http://example.com?lang=de). Bạn sẽ cần đặt Ngôn ngữ hợp lệ dựa trên thông tin URL - điều này có thể được thực hiện theo nhiều cách khác nhau (ví dụ: Bộ lọc URL).
  4. Bạn có thể muốn hỗ trợ chuyển đổi ngôn ngữ (chọn ngôn ngữ từ trình đơn thả xuống hoặc thứ gì đó), tuy nhiên tôi sẽ không khuyên bạn sử dụng (trừ khi được kết hợp với điểm 3).

JSTL cách tiếp cận có thể là đủ nếu bạn chỉ muốn hỗ trợ phương pháp đầu tiên hoặc nếu bạn không có kế hoạch để thêm bất kỳ phụ thuộc bổ sung (như Spring Framework).

Trong khi chúng ta đang ở Spring Framework nó có khá một vài tính năng thú vị mà bạn có thể sử dụng cả hai để phát hiện Locale (như CookieLocaleResolver, AcceptHeaderLocaleResolver, SessionLocaleResolverLocaleChangeInterceptor) và chuỗi externalizing và tin nhắn định dạng (xem spring:message tab).
Spring Framework sẽ cho phép bạn thực hiện khá dễ dàng tất cả các tình huống trên và đó là lý do tại sao tôi thích nó hơn.

Chuỗi externalization

Đây là cái gì đó nên được dễ dàng, phải không? Vâng, chủ yếu là - chỉ cần sử dụng thẻ thích hợp. Vấn đề duy nhất bạn có thể phải đối mặt là khi nói đến các văn bản bên ngoài (JavaScript) bên ngoài. Có một số cách tiếp cận có thể có, nhưng hãy để tôi đề cập đến hai phương pháp sau:

  1. Có mỗi chuỗi văn bản dịch JSP (có thẻ thông báo) và chỉ truy cập vào mảng đó trong mã máy khách. Đây là cách tiếp cận dễ dàng hơn nhưng ít bảo trì hơn - bạn sẽ cần viết các chuỗi hợp lệ từ các trang hợp lệ (các trang thực sự tham chiếu các kịch bản phía máy khách của bạn). Tôi đã làm điều đó trước đây và tin tôi, đây không phải là điều bạn muốn làm trong ứng dụng lớn (nhưng nó có lẽ là giải pháp tốt nhất cho một ứng dụng nhỏ).
  2. Một cách tiếp cận khác có thể có vẻ khó khăn về nguyên tắc nhưng thực tế nó dễ xử lý hơn trong tương lai. Ý tưởng là tập trung chuỗi ở phía máy khách (di chuyển chúng sang một số tệp JavaScript phổ biến). Sau đó, bạn sẽ cần phải thực hiện Servlet của riêng bạn sẽ trả về kịch bản này theo yêu cầu - nội dung cần được dịch. Bạn sẽ không thể sử dụng JSTL ở đây, bạn sẽ cần phải nhận chuỗi từ Resource Bundles trực tiếp.
    Nó dễ dàng hơn nhiều để duy trì, bởi vì bạn sẽ có một, điểm trung tâm để thêm chuỗi có thể dịch.

concatenations

Tôi ghét phải nói rằng, nhưng concatenations đang thực sự đau đớn từ quan điểm Localizability. Chúng rất phổ biến và hầu hết mọi người không nhận ra nó.

Vậy thì kết nối là gì?

Về nguyên tắc, mỗi câu tiếng Anh cần được dịch sang ngôn ngữ đích.Vấn đề là, nó xảy ra nhiều lần thông điệp được dịch chính xác sử dụng thứ tự từ khác với đối tác tiếng Anh của nó (vì vậy tiếng Anh "Chính sách bảo mật" được dịch sang tiếng Ba Lan "Polityka bezpieczeństwa" - "policy" là "polityka" - thứ tự khác).

OK, nhưng cách nó liên quan đến phần mềm?

Trong ứng dụng web mà bạn có thể nối Strings như thế này:

String securityPolicy = "Security " + "policy"; 

hay như thế này:

<p><span style="font-weight:bold">Security</span> policy</p> 

Cả hai sẽ có vấn đề. Trong trường hợp đầu tiên, bạn cần phải sử dụng phương thức MessageFormat.format() và các chuỗi bên ngoài như (ví dụ) "Security {0}""policy", trong trường hợp sau, bạn sẽ bên ngoài nội dung của toàn bộ đoạn (thẻ p), bao gồm thẻ khoảng. Tôi biết rằng điều này là đau đớn cho người dịch nhưng thực sự không có cách nào tốt hơn.
Đôi khi bạn phải sử dụng nội dung động trong đoạn của mình - thẻ định dạng JSTL fmt: format cũng sẽ giúp bạn ở đây (nó hoạt động vôi MessageFormat ở phía bên phụ trợ).

Layouts

Trong ứng dụng cục bộ, nó thường xảy ra rằng chuỗi dịch là cách dài hơn những tiếng Anh. Kết quả có thể trông rất xấu xí. Bằng cách nào đó, bạn sẽ cần phải sửa chữa phong cách. Có hai phương pháp tiếp cận lại:

  1. Khắc phục sự cố khi chúng xảy ra bằng cách điều chỉnh các kiểu phổ biến (và cầu nguyện sẽ không phá vỡ các ngôn ngữ khác). Điều này là rất đau đớn để duy trì.
  2. Triển khai Cơ chế địa phương hóa CSS. Cơ chế mà tôi đang nói đến sẽ phân phối tệp CSS độc lập, mặc định bằng ngôn ngữ và ghi đè theo ngôn ngữ. Ý tưởng là phải ghi đè lên tệp CSS cho từng ngôn ngữ, để bạn có thể điều chỉnh bố cục theo yêu cầu (chỉ cho một ngôn ngữ). Để làm điều đó, tệp CSS mặc định, cũng như các trang JSP không được chứa !important từ khóa bên cạnh bất kỳ định nghĩa kiểu nào. Nếu bạn thực sự phải sử dụng nó, hãy chuyển chúng sang en.css dựa trên ngôn ngữ - điều này sẽ cho phép các ngôn ngữ khác sửa đổi chúng.

vấn đề cụ thể Văn hóa

Tránh sử dụng đồ họa, màu sắc và âm thanh có thể được cụ thể cho văn hóa phương Tây. Nếu bạn thực sự cần nó, vui lòng cung cấp phương tiện địa phương hóa. Tránh đồ họa nhạy cảm hướng (vì đây sẽ là một vấn đề khi bạn cố gắng bản địa hóa để nói tiếng Ả Rập hoặc tiếng Do Thái). Ngoài ra, đừng cho rằng toàn thế giới đang sử dụng cùng một số (tức là không đúng đối với tiếng Ả Rập).

Ngày và múi giờ

Xử lý số ngày trong thời gian trong Java là để nói rằng ít nhất không dễ dàng. Nếu bạn không hỗ trợ bất kỳ điều gì khác ngoài Lịch Gregorian, bạn có thể gắn bó với các lớp Ngày và Lịch được xây dựng sẵn. Bạn có thể sử dụng JSTL fmt: timeZone, fmt: formatDate và fmt: parseDate để đặt đúng múi giờ, định dạng và phân tích ngày trong JSP.

tôi đề nghị sử dụng fmt: formatDate như thế này:

<fmt:formatDate value="${someController.somedate}" 
    timeZone="${someController.detectedTimeZone}" 
    dateStyle="default" 
    timeStyle="default" /> 

Điều quan trọng là ngày bí mật và thời gian là múi giờ hợp lệ (cuối của người dùng). Ngoài ra nó là khá quan trọng để chuyển đổi nó sang định dạng dễ hiểu - đó là lý do tại sao tôi đề nghị phong cách định dạng mặc định.
BTW. Phát hiện múi giờ không phải là điều dễ dàng, vì trình duyệt web không quá đẹp để gửi bất kỳ thứ gì. Thay vào đó, bạn có thể thêm lĩnh vực múi giờ ưa thích các ưu đãi tài khoản (nếu có) hoặc nhận múi giờ hiện tại bù đắp từ trình duyệt web thông qua phía client script (xem Date object's methods)

số và tiền tệ

số cũng như tiền tệ phải được chuyển đổi sang định dạng địa phương. Nó được thực hiện theo cách tương tự như định dạng ngày (phân tích cú pháp cũng được thực hiện tương tự):

<fmt:formatNumber value="1.21" type="currency"/> 

điệp Compound

Bạn đã đã được cảnh báo không để nối chuỗi. Thay vào đó, bạn có thể sử dụng MessgageFormat. Tuy nhiên, tôi phải nói rằng bạn nên giảm thiểu việc sử dụng các thông điệp ghép. Đó là vì quy tắc ngữ pháp mục tiêu khá khác nhau, vì vậy người dịch có thể không chỉ cần đặt lại câu (điều này sẽ được giải quyết bằng cách sử dụng trình giữ chỗ và MessageFormat.format()), nhưng dịch toàn bộ câu theo cách khác nhau dựa trên những gì sẽ được thay thế . Hãy để tôi cung cấp cho bạn một số ví dụ:

// Multiple plural forms 
English: 4 viruses found. 
Polish: Znaleziono 4 wirusy. **OR** Znaleziono 5 wirusów. 

// Conjugation 
English: Program encountered incorrect character | Application encountered incorrect character. 
Polish: Program napotkał nieznaną literę | Aplikacja napotkała nieznaną literę. 

Mã hóa ký tự

Nếu bạn đang có kế hoạch để bản địa hoá sang ngôn ngữ mà không hỗ trợ trang mã ISO 8859-1, bạn sẽ cần phải hỗ trợ Unicode - sản phẩm tốt nhất cách là đặt mã hóa trang thành UTF-8. Tôi đã thấy những người làm việc đó như thế này:

<%@ page contentType="text/html; charset=UTF-8" %> 

tôi phải cảnh báo bạn: đây là không đủ. Bạn thực sự cần phải tuyên bố này:

<%@page pageEncoding="UTF-8" %> 

Ngoài ra, bạn vẫn sẽ cần phải khai báo mã trong tiêu đề trang, chỉ để được ở bên an toàn:

<META http-equiv="Content-Type" content="text/html;charset=UTF-8"> 

Danh sách Tôi đưa cho bạn là chưa đầy đủ nhưng đây là điểm khởi đầu tốt. Chúc may mắn :)

+0

Cảm ơn bạn đã câu trả lời xuất sắc. Thật không may, các ứng dụng tôi đang sử dụng là khổng lồ (đó là một dự án rất cũ). Mùa xuân đã được giới thiệu đôi khi vào dự án (đó là ~ 10 năm iirc) và tôi đang sử dụng hỗ trợ i18n của Spring trong một phần của ứng dụng làm cho việc sử dụng các chuỗi. Thật không may, nó không có vẻ như bất cứ ai đã thực sự nghĩ về i18n vì vậy nó xuất hiện mà tôi đang bị mắc kẹt với một nhiệm vụ khá dài và tẻ nhạt ... – NRaf

1

Bạn có thể thực hiện chính xác việc này bằng cách sử dụng thư viện thẻ chuẩn JSTL với thẻ. Lấy một bản sao của đặc tả JSTL, đọc các chương i8N, thảo luận về văn bản chung + ngày, thời gian, tiền tệ. Rất rõ ràng bằng văn bản và cho bạn thấy làm thế nào bạn có thể làm tất cả với thẻ. Bạn cũng có thể đặt những thứ như Locale theo lập trình

1

Bạn không (và không nên) cần có tệp JSP riêng cho mỗi miền địa phương. Nhiệm vụ khó khăn là để tìm ra các phím mà arent i18n-ed và di chuyển chúng vào một tập tin cho mỗi miền địa phương, nói, messages_en.properties, messages_fr.properties và như vậy.

Tính toán cục bộ có thể xảy ra ở nhiều nơi tùy thuộc vào logic của bạn. Chúng tôi hỗ trợ ngôn ngữ người dùng được lưu trữ trong cơ sở dữ liệu cũng như ngôn ngữ trình duyệt. Mọi yêu cầu đi vào ứng dụng của bạn sẽ có tiêu đề "Chấp nhận ngôn ngữ" cho biết ngôn ngữ trình duyệt của bạn đã được định cấu hình, với tùy chọn, tức là tiếng Nhật trước và sau đó là tiếng Anh. Nếu đó là trường hợp, ứng dụng sẽ đọc messages_ja.properties và cho các khóa không có trong tệp đó, dự phòng là messages_en.properties. Điều tương tự cũng có thể đúng đối với miền địa phương của người dùng được lưu trữ bên trong cơ sở dữ liệu. Xin lưu ý rằng tiêu chuẩn chỉ là để chuyển đổi ngôn ngữ trong trình duyệt và mong đợi nội dung là i18n-ed. (Ban đầu chúng tôi bắt đầu với việc lưu trữ ngôn ngữ trong cơ sở dữ liệu và sau đó chuyển sang hỗ trợ ngôn ngữ từ trình duyệt). Ngoài ra, bạn sẽ cần một mặc định anyway như dịch giả bỏ lỡ sao chép các phím và giá trị từ tiếng Anh (tập tin ngôn ngữ chính) sang các ngôn ngữ khác, vì vậy bạn sẽ cần phải mặc định để tiếng Anh cho các giá trị không có trong các tập tin khác.

Ive cũng đã tìm thấy mygengo rất hữu ích khi cung cấp công việc dịch cho những người khác biết một ngôn ngữ cụ thể, nó đã tiết kiệm cho chúng tôi rất nhiều thời gian.

Các vấn đề liên quan