feat(localization): Add en/vi language
This commit is contained in:
400
I18N_USAGE_GUIDE.md
Normal file
400
I18N_USAGE_GUIDE.md
Normal file
@@ -0,0 +1,400 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user