Chuyển đổi encoding tiếng Nhật trong NodeJS




Bài đăng trên chuyên mục Lập trình vào ngày 14 tháng 1 năm 2022 bởi manhhomienbienthuy. Chỉnh sửa lần cuối lúc 09:32:26 ngày 17 tháng 1 năm 2022 (JST).

Chuyển đổi encoding tiếng Nhật trong NodeJS

Vâng, lại là vấn đề encoding với tiếng Nhật 🤣 Dù mình đã chuyển sang làm việc khác, với một ngôn ngữ khác (cụ thể là NodeJS code bằng TypeScript) thì vẫn không chạy đâu cho khỏi nắng. Còn làm với Nhật thì chắc mình còn phải sống chung với lũ dài dài.

Vấn đề

Tương tự như vấn đề mình gặp không lâu trước đây, lần này là với ngôn ngữ khác và bối cảnh khác đi một chút.

Số là mình cần làm một server để nhận request trả về từ các dịch vụ bên ngoài. Server xử lý cũng đơn giản thôi, nhận request (qua HTTP POST), lấy thông tin được gửi cho server rồi gọi API. Server đơn giản nên team quyết định dùng NodeJS, code bằng TypeScript với thư viện express 😇.

Mọi chuyện đều ổn cho đến khi gặp một dịch vụ (không tiện nói tên) không chịu dùng utf-8 mà vẫn dùng Shift_JIS 🥲 như "truyền thống" của người Nhật bao lâu nay. Và vì là dịch vụ bên ngoài, mình dùng thì phải theo nó chứ không bắt nó theo mình được 🤬, nên bắt buộc mình phải tìm cách xử lý được dữ liệu dạng Shift_JIS và chuyển thành utf-8 để xử lý bên hệ thống của mình.

Trước đây, với Python thì việc này không có gì khó, vì Python hỗ trợ chuyển đổi dữ liệu của các bảng mã rất dễ dàng 🥰. Thế nhưng với NodeJS thì khó khăn hơn, do việc chuyển đổi này không hề được hỗ trợ sẵn 😤.

Giải quyết

Chuyển đổi encoding

Nói nghe dark deep thế thôi chứ giải quyết vấn đề này không có gì cao siêu cả 🧐 Mình có thử một số cách "native" của NodeJS (dùng escape, unescape, decodeURIComponent) nhưng không thành công. Và thế là mình phải tìm đến các thư viện bên ngoài.

Sau khi tìm hiểu một hồi (không lâu lắm 🤪) thì mình lọc ra được 3 thư viện có thể dùng được:

  • encoding-japanese: chuyển đổi encoding tiếng Nhật ổn, tương đối gọn nhẹ nhưng 2 năm rồi không update 😫.
  • iconv-lite: chuyển đổi tiếng Nhật và nhiều loại encoding khác ổn, gọn nhẹ và được update khá thường xuyên 🤤.
  • icon: chuyển đổi rất nhiều encoding khác nhau (đầy đủ nhất trong các thư viện), được update thường xuyên tuy nhiên lại hơi nặng 😵‍💫.

Xét thấy vấn đề đang gặp phải (và nhiều yếu tố khác nữa 😎) thì mình quyết định xử dụng iconv-lite. Toàn bộ code để chuyển đổi encoding như dưới đây:

import iconv from 'iconv-lite';

const urlEncodedToBytes = (encoded: string) => {
  let decoded = Buffer.from('');
  for (let i = 0; i < encoded.length; i++) {
    if (encoded[i] === '%') {
      const charBuf = Buffer.from(`${encoded[i + 1]}${encoded[i + 2]}`, 'hex');
      decoded = Buffer.concat([decoded, charBuf]);
      i += 2;
    } else {
      const charBuf = Buffer.from(encoded[i]);
      decoded = Buffer.concat([decoded, charBuf]);
    }
  }
  return decoded;
};

const data = '%93%FA%96%7B%8C%EA'; // url encoded của từ 日本語
const buffer = urlEncodedToBytes(data);
console.log(iconv.decode(converted, 'sjis'))
// 日本語

Cấu hình server nodeJS

Phần chuyển đổi encoding đã xong, lúc này lại phát sinh một vấn đề mới 😫, đó là làm thế nào để nhận dữ liệu thô (mã hóa bằng Shift_JIS).

Vì mình có kiểm tra các request đến server thì dữ liệu tiếng Nhật bị chuyển đổi tự động (khá chắc là chuyển đổi trên server) luôn rồi (nhưng chuyển đổi sai mới chết 😡). Tất cả các chữ tiếng Nhật đều được chuyển thành 0xFFFF nên lấy dữ liệu đó ra không dùng được 😹.

Cũng may là vấn đề này không khó lắm, quan trọng là phải Google đúng keyword 🥳. Giờ mình cũng quên mất tìm như thế nào rồi, nhưng cũng mất kha khá thời gian (lâu hơn nhiều so với tìm hiểu thư viện ở trên).

Kết quả, express hỗ trợ việc này khá đơn giản bằng config như thế này:

import express from 'express';

const app = express();
app.use(express.urlencoded({ extended: true }));
...

Vậy là xong, mọi request đến server sẽ không được chuyển đổi tự động nữa mà nhận luôn dữ liệu thô (dưới dạng url encoded với các dấu %).

works

#NodeJS #JavaScript #encoding #unicode #utf-8 #Shift_JIS #converting

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.

Welcome




manhhomienbienthuy

Xin chào. Tôi là manhhomienbienthuy, nickname khác là naa. Đây là thế giới của tôi, chào mừng đến với thế giới của tôi…

Bài viết liên quan




Bài viết mới




Chuyên mục




Lưu trữ theo năm




Câu nói yêu thích




There's a big difference between knowing the name of something and knowing something.

– Richard P. Feynman –

Thông tin liên hệ




Cảm ơn bạn đã quan tâm blog của tôi. Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.