400 lines
9.3 KiB
Markdown
400 lines
9.3 KiB
Markdown
# Internationalization (i18n) Usage Guide
|
|
|
|
This guide explains how to use the internationalization (i18n) features in your Ship Monitoring System built with Umi Max.
|
|
|
|
## 📋 Table of Contents
|
|
|
|
1. [Setup Overview](#setup-overview)
|
|
2. [File Structure](#file-structure)
|
|
3. [Basic Usage](#basic-usage)
|
|
4. [Advanced Usage](#advanced-usage)
|
|
5. [Custom Hook](#custom-hook)
|
|
6. [Language Switcher](#language-switcher)
|
|
7. [Best Practices](#best-practices)
|
|
8. [Troubleshooting](#troubleshooting)
|
|
|
|
## 🚀 Setup Overview
|
|
|
|
### Configuration Files
|
|
|
|
**`.umirc.ts`** - Main i18n configuration:
|
|
```typescript
|
|
locale: {
|
|
default: 'vi-VN',
|
|
baseNavigator: false,
|
|
antd: true,
|
|
title: false,
|
|
baseSeparator: '-',
|
|
locales: [
|
|
['vi-VN', 'Tiếng Việt'],
|
|
['en-US', 'English'],
|
|
],
|
|
},
|
|
```
|
|
|
|
**`src/app.tsx`** - Runtime configuration:
|
|
```typescript
|
|
// Enable locale in menu
|
|
menu: {
|
|
locale: true,
|
|
},
|
|
|
|
// Add language switcher to header
|
|
rightContentRender: () => [
|
|
<LanguageSwitcher key="language-switcher" />,
|
|
],
|
|
```
|
|
|
|
## 📁 File Structure
|
|
|
|
```
|
|
src/
|
|
├── locales/ # Translation files
|
|
│ ├── vi-VN.ts # Vietnamese translations
|
|
│ └── en-US.ts # English translations
|
|
├── components/
|
|
│ ├── LanguageSwitcher/ # Language switcher component
|
|
│ │ └── index.tsx
|
|
│ └── I18nExamples/ # Example components
|
|
│ ├── index.tsx
|
|
│ ├── BasicUsage.tsx
|
|
│ ├── FormValidation.tsx
|
|
│ └── CustomHookExample.tsx
|
|
├── hooks/
|
|
│ └── useTranslation.ts # Custom i18n hook
|
|
└── app.tsx # Runtime configuration
|
|
```
|
|
|
|
## 🔧 Basic Usage
|
|
|
|
### 1. Using `useIntl` Hook
|
|
|
|
```typescript
|
|
import { useIntl } from '@umijs/max';
|
|
import React from 'react';
|
|
|
|
const MyComponent: React.FC = () => {
|
|
const intl = useIntl();
|
|
|
|
return (
|
|
<div>
|
|
{/* Simple translation */}
|
|
<h1>{intl.formatMessage({ id: 'common.save' })}</h1>
|
|
|
|
{/* Translation with variables */}
|
|
<p>{intl.formatMessage(
|
|
{ id: 'validation.minLength' },
|
|
{ min: 6 }
|
|
)}</p>
|
|
</div>
|
|
);
|
|
};
|
|
```
|
|
|
|
### 2. Using `FormattedMessage` Component
|
|
|
|
```typescript
|
|
import { FormattedMessage } from '@umijs/max';
|
|
import React from 'react';
|
|
|
|
const MyComponent: React.FC = () => {
|
|
return (
|
|
<div>
|
|
{/* Simple translation */}
|
|
<FormattedMessage id="common.save" />
|
|
|
|
{/* Translation with variables */}
|
|
<FormattedMessage
|
|
id="validation.minLength"
|
|
values={{ min: 6 }}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
```
|
|
|
|
## 🎯 Advanced Usage
|
|
|
|
### 1. Form Validation with i18n
|
|
|
|
```typescript
|
|
import { Form, Input, Button } from 'antd';
|
|
import { useIntl } from '@umijs/max';
|
|
import React from 'react';
|
|
|
|
const MyForm: React.FC = () => {
|
|
const intl = useIntl();
|
|
|
|
return (
|
|
<Form>
|
|
<Form.Item
|
|
label={intl.formatMessage({ id: 'common.username' })}
|
|
name="username"
|
|
rules={[
|
|
{
|
|
required: true,
|
|
message: intl.formatMessage({ id: 'validation.required' }),
|
|
},
|
|
{
|
|
type: 'email',
|
|
message: intl.formatMessage({ id: 'validation.email' }),
|
|
},
|
|
]}
|
|
>
|
|
<Input
|
|
placeholder={intl.formatMessage({ id: 'common.username' })}
|
|
/>
|
|
</Form.Item>
|
|
</Form>
|
|
);
|
|
};
|
|
```
|
|
|
|
### 2. Conditional Rendering
|
|
|
|
```typescript
|
|
import { useIntl } from '@umijs/max';
|
|
import React from 'react';
|
|
|
|
const LocalizedContent: React.FC = () => {
|
|
const intl = useIntl();
|
|
|
|
return (
|
|
<div>
|
|
{intl.locale === 'vi-VN' ? (
|
|
<p>Chào mừng đến với hệ thống!</p>
|
|
) : (
|
|
<p>Welcome to the system!</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
```
|
|
|
|
## 🪝 Custom Hook Usage
|
|
|
|
For more convenient usage, use the custom `useTranslation` hook:
|
|
|
|
```typescript
|
|
import { useTranslation } from '@/hooks/useTranslation';
|
|
import React from 'react';
|
|
|
|
const MyComponent: React.FC = () => {
|
|
const { t, isLocale, formatDate, formatNumber } = useTranslation();
|
|
|
|
return (
|
|
<div>
|
|
{/* Simple translation */}
|
|
<h1>{t('common.save')}</h1>
|
|
|
|
{/* Translation with variables */}
|
|
<p>{t('validation.minLength', { min: 6 })}</p>
|
|
|
|
{/* Locale detection */}
|
|
{isLocale('vi-VN') && <p>Xin chào!</p>}
|
|
|
|
{/* Date formatting */}
|
|
<p>{formatDate(new Date(), {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})}</p>
|
|
|
|
{/* Number formatting */}
|
|
<p>{formatNumber(1234.56, {
|
|
style: 'currency',
|
|
currency: 'VND'
|
|
})}</p>
|
|
</div>
|
|
);
|
|
};
|
|
```
|
|
|
|
## 🌐 Language Switcher
|
|
|
|
The language switcher component is automatically included in the header. You can also use it manually:
|
|
|
|
```typescript
|
|
import LanguageSwitcher from '@/components/LanguageSwitcher';
|
|
import React from 'react';
|
|
|
|
const Header: React.FC = () => {
|
|
return (
|
|
<div>
|
|
<LanguageSwitcher type="dropdown" />
|
|
{/* or */}
|
|
<LanguageSwitcher type="button" size="small" />
|
|
</div>
|
|
);
|
|
};
|
|
```
|
|
|
|
### Language Switcher Props
|
|
|
|
| Prop | Type | Default | Description |
|
|
|------|------|---------|-------------|
|
|
| `className` | `string` | - | Additional CSS classes |
|
|
| `type` | `'dropdown' \| 'button'` | `'dropdown'` | Display type |
|
|
| `size` | `'small' \| 'middle' \| 'large'` | `'middle'` | Component size |
|
|
|
|
## 📝 Translation Keys
|
|
|
|
Translation keys follow a hierarchical structure using dot notation:
|
|
|
|
```
|
|
common.save // Save button text
|
|
auth.login.title // Login page title
|
|
map.ship.status.online // Ship online status
|
|
validation.required // Validation message
|
|
```
|
|
|
|
### Adding New Translations
|
|
|
|
1. **Vietnamese** (`src/locales/vi-VN.ts`):
|
|
```typescript
|
|
export default {
|
|
// Add new key
|
|
'my.new.key': 'Nội dung mới',
|
|
};
|
|
```
|
|
|
|
2. **English** (`src/locales/en-US.ts`):
|
|
```typescript
|
|
export default {
|
|
// Add corresponding English translation
|
|
'my.new.key': 'New content',
|
|
};
|
|
```
|
|
|
|
## ✅ Best Practices
|
|
|
|
### 1. **Consistent Key Naming**
|
|
- Use dot notation for hierarchical structure
|
|
- Group related translations together
|
|
- Use descriptive, meaningful names
|
|
- Example: `'map.ship.status.online'` instead of `'online'`
|
|
|
|
### 2. **Variable Interpolation**
|
|
- Use descriptive variable names
|
|
- Provide context for variables
|
|
- Example: `'validation.minLength': 'Minimum {min} characters required'`
|
|
|
|
### 3. **Component Organization**
|
|
- Group translations by feature/module
|
|
- Keep related keys together
|
|
- Use consistent prefixes for modules
|
|
|
|
### 4. **Performance Considerations**
|
|
- Use the `useTranslation` hook for better performance
|
|
- Avoid inline `formatMessage` calls in render loops
|
|
- Cache translated strings when used frequently
|
|
|
|
### 5. **Translation Maintenance**
|
|
- Keep all translations in sync
|
|
- Add new keys to both language files
|
|
- Review translations regularly for consistency
|
|
|
|
## 🛠 Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### 1. **Translation not appearing**
|
|
```typescript
|
|
// ❌ Wrong
|
|
<FormattedMessage id="wrong.key" />
|
|
|
|
// ✅ Correct - check if key exists in locale files
|
|
<FormattedMessage id="common.save" />
|
|
```
|
|
|
|
#### 2. **Locale not changing**
|
|
- Ensure `localStorage.setItem('umi_locale', locale)` is called
|
|
- Check that locale is properly configured in `.umirc.ts`
|
|
- Verify that translation files exist for the target locale
|
|
|
|
#### 3. **Missing Ant Design translations**
|
|
```typescript
|
|
// In .umirc.ts
|
|
locale: {
|
|
antd: true, // Ensure this is enabled
|
|
}
|
|
```
|
|
|
|
#### 4. **TypeScript errors**
|
|
```typescript
|
|
// Ensure proper import
|
|
import { useIntl } from '@umijs/max';
|
|
|
|
// Check if translation key exists in locale files
|
|
const intl = useIntl();
|
|
const text = intl.formatMessage({ id: 'existing.key' });
|
|
```
|
|
|
|
### Debug Mode
|
|
|
|
To debug translation issues, add logging to your component:
|
|
|
|
```typescript
|
|
import { useIntl } from '@umijs/max';
|
|
import React from 'react';
|
|
|
|
const DebugComponent: React.FC = () => {
|
|
const intl = useIntl();
|
|
|
|
console.log('Current locale:', intl.locale);
|
|
console.log('Available messages:', intl.messages);
|
|
|
|
return <div>Check console for debug info</div>;
|
|
};
|
|
```
|
|
|
|
## 🔄 Testing Translations
|
|
|
|
To test your i18n implementation:
|
|
|
|
1. **Manual Testing**:
|
|
- Switch languages using the language switcher
|
|
- Verify all text updates correctly
|
|
- Check forms with validation messages
|
|
|
|
2. **Component Testing**:
|
|
```typescript
|
|
import { render } from '@testing-library/react';
|
|
import { I18nProvider } from '@umijs/max';
|
|
import MyComponent from './MyComponent';
|
|
|
|
// Test with different locales
|
|
render(
|
|
<I18nProvider locale="en-US">
|
|
<MyComponent />
|
|
</I18nProvider>
|
|
);
|
|
```
|
|
|
|
3. **Translation Coverage**:
|
|
- Ensure all UI text is translatable
|
|
- Check that no hardcoded strings remain
|
|
- Verify all keys exist in both language files
|
|
|
|
## 📚 Additional Resources
|
|
|
|
- [Umi Max i18n Documentation](https://umijs.org/docs/max/i18n)
|
|
- [React Intl Documentation](https://formatjs.io/docs/react-intl/)
|
|
- [Ant Design i18n Guide](https://ant.design/docs/react/internationalization)
|
|
|
|
## 🎉 Summary
|
|
|
|
Your i18n setup is now complete! You have:
|
|
|
|
✅ **Configuration**: Umi Max i18n properly configured
|
|
✅ **Translations**: Vietnamese and English translation files
|
|
✅ **Components**: Language switcher and example components
|
|
✅ **Custom Hook**: Simplified i18n API
|
|
✅ **Best Practices**: Guidelines for maintainable translations
|
|
|
|
The system is ready for multi-language support and can be easily extended with additional languages or translations as needed.
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-01-20
|
|
**Version**: 1.0.0 |